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

5

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?

Redingote answered 18/4, 2013 at 6:1 Comment(4)
@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.Crisis
Apparently, it's not true that NSLog(@"%i", myInt); is sufficient because you will get the error message as I've shown above.Redingote
@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).Crisis
Ahh, I see. I didn't know iOS and OSX would make the NSInteger different in bit and type.Redingote
S
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.

Stcyr answered 18/4, 2013 at 6:43 Comment(4)
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?Stalag
@BartSimpson: With an explicit case to "long", as in NSLog(@"%ld", (long) myInt), it works on 32-bit and 64-bit correctly.Stcyr
@MartinR if we are casting, why not just use long in the first place?Stillhunt
@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.Stcyr
D
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.

Diagenesis answered 18/4, 2013 at 7:29 Comment(3)
So now that this hack is required, is it still best practice to use NSInteger in the first place?Stillhunt
@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.Diagenesis
It is best practice to use NSInteger, because there are good reasons why it is defined the way it is defined.Dermatosis
D
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

Degraw answered 30/1, 2014 at 11:15 Comment(1)
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.Accommodation
G
0

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

Gandzha answered 23/6, 2016 at 17:44 Comment(0)
W
-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.

Warrenwarrener answered 17/3, 2016 at 11:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.