2

I am going through a former employees code and about 20 of these warnings show up:

Values of type 'NSUInteger' should not be used as format arguments; add an explicit cast to 'unsigned long' instead

one part of the code where this arises is:

NSUInteger Length;

With:

- (NSString *) description {

    // If no value was given, display type
    if([Value length] == 0)
    {
        NSString *type = @"";

        switch (Type) {
            case DTCompType_Year: type = @"year";
                break;
            case DTCompType_Month: type = @"month";
                break;            
            case DTCompType_Day: type = @"day";
                break;            
            case DTCompType_Hour: type = @"hour";
                break;            
            case DTCompType_Minute: type = @"minute";
                break;            
            case DTCompType_Second: type = @"second";
                break;            
            case DTCompType_Meridiem: type = @"meridiem";
                break;            
            case DTCompType_MonthName: type = @"month_name";
                break;
            case DTCompType_DayOfTheWeek: type = @"day_of_the_week";
                break;
            case DTCompType_Undefined: type = @"undefined";
                break;            
        }

        return Length == 0 ? [NSString stringWithFormat:@"[%@]", type] : 
        [NSString stringWithFormat:@"[%@:%i]", type, Length];
    }

No where in apples documentation can I find %i

Apple's Documentation

I have never worked with Objective-C before, and now I have to update this app. I understand that this needs to become an unsigned long, but I don't want to start changing things without knowing why. The app works just fine as is, so are there any inherent consequences for changing these to unsigned long? or even changing the format specifier from %i to %lu?

From what I've read, it could be a matter of the platform. (32-bit vs 64-bit)

This was developed for an iPad 2 in iOS7, and we just upgraded the SDK to iOS8.

I found this post: NSUInteger should not be used in format strings? which has given me some guidance, but I need more clarification.

Community
  • 1
  • 1
  • `%i` is the same as `%d` - http://pubs.opengroup.org/onlinepubs/009695399/functions/printf.html – Hot Licks Nov 11 '14 at 18:28
  • 1
    Leading upper case should not be used for variable names (but of course using a variable name of `length` is not a good idea either). – Hot Licks Nov 11 '14 at 18:29
  • 1
    The problem with NSUInteger (aside from being unsigned and therefore "odd") is that it can't make up its mind whether it's an int, long, or a long long. – Hot Licks Nov 11 '14 at 18:30
  • What further guidance do you need that isn't in the other question you referenced? That answer tells you what to do to fix the issue. – rmaddy Nov 11 '14 at 18:31
  • 1
    The easiest (but possibly suboptimal) solution in this situation might be to let the machine decide what to do by making an `NSNumber` for printing. `[NSString stringWithFormat:@"[%@:%@]", type, @(Length)]`. – Ian MacDonald Nov 11 '14 at 18:32
  • @IanMacDonald, I tested that out and it did seem to remove the warning. I'll try that out in the reset of the warnings. I guess the only thing I need to keep an eye out for is if it was required to be a certain type somewhere else in the application. – grenadier89 Nov 11 '14 at 18:43
  • possible duplicate of [NSInteger and NSUInteger in a mixed 64bit / 32bit environment](http://stackoverflow.com/questions/20355439/nsinteger-and-nsuinteger-in-a-mixed-64bit-32bit-environment) – i_am_jorf Nov 11 '14 at 18:44
  • @HotLicks, thank you, that does help knowing that %i and %d are the same thing. So I'm guessing %i has been deprecated? It's gonna take me a long time to go through this entire application to learn exactly what's going on (and learn Objective-C of course). Would there be any reason to do something like this intentionally? – grenadier89 Nov 11 '14 at 18:48
  • Actually, I think `%i` is "new" -- `%d` has been around for about 40 years, but I'd never seen `%i` until about 5 years ago. – Hot Licks Nov 11 '14 at 19:13
  • (To do something like what intentionally -- learn Objective-C?? I suppose if someone is holding a gun to your head. ;) ) – Hot Licks Nov 11 '14 at 19:14
  • LOL! I though %i was old since I couldn't find it anywhere. No I meant to explicitly type `%i` instead of `%lu`. Apparently the guy who wrote app had been working with Objective-C for a really long time, unless he didn't know as much as he thought! I just have my boss holding an unsigned paycheck over my head if I don't update this app. So I guess that's like a gun to my head ;) – grenadier89 Nov 11 '14 at 19:29

2 Answers2

1

%i is equivalent to %d. Technically, you should have been using %u anyway. The problem is, as you suspect, 32-bit vs 64-bit; NS[U]Integer is [unsigned] int on 32-bit builds, but [unsigned] long on 64-bit ones. Because the iPhone is little-endian, it will "work" as long as the %i/d/u is the last format specified, but it's still wrong. You should cast the argument to be the type the format specifier expects (int/long/unsigned/unsigned long), as the warning message tells you to.

From <objc/NSObjCRuntime.h>:

#if __LP64__ || (TARGET_OS_EMBEDDED && !TARGET_OS_IPHONE) || TARGET_OS_WIN32 || NS_BUILD_32_LIKE_64
typedef long NSInteger;
typedef unsigned long NSUInteger;
#else
typedef int NSInteger;
typedef unsigned int NSUInteger;
#endif
Kevin
  • 53,822
  • 15
  • 101
  • 132
  • The problem is that since NSUInteger can flip-flop, depending on the compile environment, there isn't a "correct" formatting code for it. Better to cast to a vanilla C type and format that. – Hot Licks Nov 11 '14 at 19:16
  • I'll give this a try too. As stated earlier, a possibility is to use NSNumber, which is the most generic way of handling it, but I'm guessing that could lead to unintended conversions when left up to the machine. I'll implement and test it this way. So would the completely proper way be to both cast the argument to `(unsigned long)Length` AND change it to `%lu`? or is it not critical to cast it as well because it will be the same either way? A very large company uses this app daily, so I'd get in big trouble if I messed things up! Thanks for your help! – grenadier89 Nov 11 '14 at 19:21
  • 1
    @grenadier89 Yes, the most proper way to handle this is to both change the argument and add the cast. – Kevin Nov 11 '14 at 19:28
1

You can use a boxed literal to allow the compiler and the NSNumber class to handle the details of converting between the various numeric types and their string representations. For example, given the following variable definition...

NSUInteger foo = 42;

...you can create an instance of NSNumber as follows:

NSNumber *myNumber = @(foo);

You can then use the %@ format specifier whenever you need to format the value of myNumber. Of course it's easy enough to instead box the original numeric value right in line:

NSString *s = [NSString stringWithFormat:@"The answer is %@", @(foo)];
jlehr
  • 15,557
  • 5
  • 43
  • 45