Nsinteger and Nsuinteger in a Mixed 64Bit/32Bit Environment

NSInteger and NSUInteger in a mixed 64bit / 32bit environment

I think the safest way is to box them into NSNumber instances.

NSLog(@"Number is %@", @(number)); // use the highest level of abstraction

This boxing doesn't usually have to create a new object thanks to tagged pointer magic.

If you really don't want to use NSNumber, you can cast primitive types manually, as others suggested:

NSLog(@"Number is %ld", (long)number); // works the same on 32-bit and 64-bit

Formatting NSUInteger with %i

%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

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

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.

String formatting %ld for NSInteger suggests (long)?

From Apple's headers:

#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

So the type of NSInteger is either int or long, depending on the target platform. You're not getting a warning when you don't cast because you're (most likely) running on a 64 bit platform. If you target a 32bit platform, you'll get a warning without the cast.

You can use %zd, which will always work without the cast on 32bit and 64bit platforms (but only for signed integers—I think).

You can do something like this and not ever worry about it (but there is a performance hit):

NSInteger myNSInteger = 12345;
[NSString stringWithFormat:@"%@", @(myNSInteger)];

NSLog/printf specifier for NSInteger?

Updated answer:

You can make use of the z and t modifiers to handle NSInteger and NSUInteger without warnings, on all architectures.

You want to use %zd for signed, %tu for unsigned, and %tx for hex.

This information comes courtesy of Greg Parker.


Original answer:

The official recommended approach is to use %ld as your specifier, and to cast the actual argument to a long.

How should I declare a long in Objective-C? Is NSInteger appropriate?

IIRC, long on the iPhone/ARM is 32 bits. If you want a guaranteed 64-bit integer, you should (indeed) use int64_t.

Strange BAD_ACCESS when printing NSInteger

The only guess here: NSInteger could be 64 bit in your case and %i (as well as %d) expects 32-bit integer, you can try %qi or (what seems to be better) cast the value explicitly.

Tagged pointers in Objective-C

OS X and iOS both use tagged pointer objects in 64-bit code. Neither currently uses any tagged pointer objects in 32-bit code, though in principle it's not impossible. The specific set of optimized classes and optimized values changes frequently. Open-source objc4/runtime/objc-internal.h describes this set of classes that was used in at least one OS version:

OBJC_TAG_NSAtom            = 0, 
OBJC_TAG_1 = 1,
OBJC_TAG_NSString = 2,
OBJC_TAG_NSNumber = 3,
OBJC_TAG_NSIndexPath = 4,
OBJC_TAG_NSManagedObjectID = 5,
OBJC_TAG_NSDate = 6,
OBJC_TAG_7 = 7


Related Topics



Leave a reply



Submit