Why does an NSInteger variable have to be cast to long when used as a format argument?

45,638

Solution 1

You get this warning if you compile on OS X (64-bit), because on that platform NSInteger is defined as long and is a 64-bit integer. The %i format, on the other hand, is for int, which is 32-bit. So the format and the actual parameter do not match in size.

Since NSInteger is 32-bit or 64-bit, depending on the platform, the compiler recommends to add a cast to long generally.

Update: Since iOS 7 supports 64-bit now as well, you can get the same warning when compiling for iOS.

Solution 2

You don't have to cast to anything if your format specifiers match your data types. See Martin R's answer for details on how NSInteger is defined in terms of native types.

So for code intended to be built for 64-bit environments, you can write your log statements like this:

NSLog(@"%ld",  myInt); 

while for 32-bit environments you can write:

NSLog(@"%d",  myInt); 

and it will all work without casts.

One reason to use casts anyway is that good code tends to be ported across platforms, and if you cast your variables explicitly it will compile cleanly on both 32 and 64 bit:

NSLog(@"%ld",  (long)myInt);

And notice this is true not just for NSLog statements, which are just debugging aids after all, but also for [NSString stringWithFormat:] and the various derived messages, which are legitimate elements of production code.

Solution 3

Instead of passing an NSInteger to NSLog, just pass an NSNumber. This will get around all the casts and choosing the right string format specifier.

NSNumber foo = @9000;
NSLog(@"foo: %@", foo);
NSInteger bar = 9001;
NSLog(@"bar: %@", @(bar));

It also works for NSUIntegers without having to worry about that. See answer to NSInteger and NSUInteger in a mixed 64bit / 32bit environment

Share:
45,638
Daniel Lee
Author by

Daniel Lee

Updated on July 08, 2022

Comments

  • Daniel Lee
    Daniel Lee almost 2 years
    NSInteger myInt = 1804809223;
    NSLog(@"%i", myInt); <==== 
    

    The code above produces an error:

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

    The corrected NSLog message is actually NSLog(@"%lg", (long) myInt);. Why do I have to convert the integer value of myInt to long if I want the value to display?

  • Bilbo Baggins
    Bilbo Baggins over 10 years
    I get this error on iOS 7. Since only the latest iPhone 5S is 64 bit if I set it as long will it cause any problem on older 32 bit devices?
  • Martin R
    Martin R over 10 years
    @BartSimpson: With an explicit case to "long", as in NSLog(@"%ld", (long) myInt), it works on 32-bit and 64-bit correctly.
  • William Entriken
    William Entriken over 10 years
    So now that this hack is required, is it still best practice to use NSInteger in the first place?
  • William Entriken
    William Entriken over 10 years
    @MartinR if we are casting, why not just use long in the first place?
  • Martin R
    Martin R over 10 years
    @FullDecent: Of course you can work with long here: long myInt = [myNumber longValue];. But many (Core)Foundation methods use NS(U)Integer as parameter or return value, so the general problem remains. Also it can make sense in your app to use NS(U)Integer to get a larger available range on 64-bit devices.
  • Monolo
    Monolo over 10 years
    @FullDecent It is only a problem in code that is interpreted runtime, such as format strings. Al compiled code takes advantage of the NSInteger typedef.
  • gnasher729
    gnasher729 about 10 years
    It is best practice to use NSInteger, because there are good reasons why it is defined the way it is defined.
  • Daniel Wood
    Daniel Wood about 10 years
    I suppose the selected answer is technically the best answer to the question, but if you want to know how to avoid casting each occurrence and prevent warnings then I find this is the best solution.