147
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?

Lucas
  • 523
  • 2
  • 10
  • 20
Daniel Lee
  • 1,501
  • 2
  • 10
  • 5
  • 1
    @DanielLee, if you use `NSLog(@"%ld", (long) myInt);`, the `long` cast is to make it match up with the `l` qualifier of `%ld`, but all of that is unnecessary as `NSLog(@"%d", myInt);` is sufficient (given that we can see that `myInt` is not `long`. Bottom line, you cast `myInt` if using long qualifier in format string, but no need to use either long string format qualifier or `long` cast here. – Rob Apr 18 '13 at 06:32
  • 1
    Apparently, it's not true that NSLog(@"%i", myInt); is sufficient because you will get the error message as I've shown above. – Daniel Lee Apr 18 '13 at 06:40
  • 2
    @DanielLee See Martin R's comment. You posted your question with iOS tag (where `NSInteger` is _not_ long) , but it sounds like you're compiling with OS X target (where `NSInteger` _is_ `long`). – Rob Apr 18 '13 at 06:47
  • Ahh, I see. I didn't know iOS and OSX would make the NSInteger different in bit and type. – Daniel Lee Apr 18 '13 at 07:55

5 Answers5

194

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.

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • 1
    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? – Bilbo Baggins Nov 01 '13 at 03:46
  • 26
    @BartSimpson: With an explicit case to "long", as in `NSLog(@"%ld", (long) myInt)`, it works on 32-bit and 64-bit correctly. – Martin R Nov 01 '13 at 05:54
  • @MartinR if we are casting, why not just use long in the first place? – William Entriken Jan 05 '14 at 17:42
  • 3
    @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. – Martin R Jan 05 '14 at 17:59
41

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.

Monolo
  • 18,205
  • 17
  • 69
  • 103
  • 1
    So now that this hack is required, is it still best practice to use NSInteger in the first place? – William Entriken Jan 05 '14 at 17:36
  • @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. – Monolo Jan 06 '14 at 09:11
  • It _is_ best practice to use NSInteger, because there are good reasons why it is defined the way it is defined. – gnasher729 May 07 '14 at 14:51
22

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

Community
  • 1
  • 1
orkoden
  • 18,946
  • 4
  • 59
  • 50
  • 2
    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. – Daniel Wood Jun 21 '14 at 07:59
0

It keeps warning while using NSLog(@"%ld", (long)myInt);, but stops warning after change declaration to long myInt = 1804809223; in iOS 10.

Yao Li
  • 2,071
  • 1
  • 25
  • 24
-2

OS X uses several data types—NSInteger, NSUInteger,CGFloat, and CFIndex—to provide a consistent means of representing values in 32- and 64-bit environments. In a 32-bit environment, NSInteger and NSUInteger are defined as int and unsigned int, respectively. In 64-bit environments, NSInteger and NSUInteger are defined as long and unsigned long, respectively. To avoid the need to use different printf-style type specifiers depending on the platform, you can use the specifiers shown in this link for both 32 bit and 64 bit environment.

Saheb Singh
  • 555
  • 4
  • 18