46

Here's my code in all it's glory:

[NSString stringWithFormat:@"Total Properties: %d", (int)[inArray count]];

Which gets me an Xcode 5.1 warning:

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

Ok so I'm confused. The value really is a 32-bit int, and I cast it to a 32-bit int. So what is this NSUInteger it's complaining about (the count I assume) and why doesn't this cast fix it?

Scott Berrevoets
  • 16,921
  • 6
  • 59
  • 80
Maury Markowitz
  • 9,082
  • 11
  • 46
  • 98
  • 1
    Are you sure that the line produces a warning? I use they cast to int all the time myself, and I never get an error. Just checked it in Xcode 5.1 with a simple example. – Michael Knudsen Mar 26 '14 at 19:58
  • To hopefully forestall confusion of future readers: this warning appears (or not) depending on the architecture for which you are compiling. – Palpatim Mar 03 '16 at 15:30

4 Answers4

107

NSUInteger and NSInteger are different lengths on 32-bit (int) and 64-bit (long). In order for one format specifier to work for both architectures, you must use a long specifier and cast the value to long:

Type    Format Specifier    Cast
----    ----------------    ----
NSInteger    %ld            long
NSUInteger   %lu            unsigned long

So, for example, your code becomes:

[NSString stringWithFormat:@"Total Properties: %lu", (unsigned long)[inArray count]];

There is very little work to do, really, because Xcode's Fix-It feature will do this for you automatically.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Thanks matt! Works *almost* like a champ, the almost being because the fix-it doesn't seem to be fixing it. – Maury Markowitz Mar 26 '14 at 19:52
  • Well, your original int cast may have confused it. I just did a huge number of fixes along these lines in my book code (last four or five commits at https://github.com/mattneub/Programming-iOS-Book-Examples), and Fix-It got most of them right. But some of them had to be done by hand. – matt Mar 26 '14 at 19:57
  • However, NSInteger/NSUInteger is the same length as long/unsigned long on both architectures. So why is the cast necessary? – user102008 Apr 02 '15 at 22:23
  • @matt: My premises are correct. If you think they are wrong please point out which statement you think is wrong and what you think is wrong with it. – user102008 Apr 03 '15 at 01:26
47

It is also possible to use the "z" and "t" modifiers for CPU-independent format strings, e.g.

NSInteger x = -1;
NSUInteger y = 99;
NSString *foo = [NSString stringWithFormat:@"NSInteger: %zd, NSUInteger: %tu", x, y];
user102008
  • 30,736
  • 10
  • 83
  • 104
Nick Lockwood
  • 40,865
  • 11
  • 112
  • 103
9

The underlying type of NSUInteger changes based on the platform: it is a 32-bit unsigned integer on 32-bit platforms, and a 64-bit unsigned integer on 64-bit platforms.

In the Platform Dependencies section on of the String Programming Guide Apple suggests that you do the following:

To avoid the need to use different printf-style type specifiers depending on the platform, you can use the specifiers shown in Table 3. Note that in some cases you may have to cast the value.

For NSUInteger use format %lu or %lx, and cast the value to unsigned long.

Hence your code needs to be changed as follows to avoid the warning:

[NSString stringWithFormat:@"Total Properties: %lu", (unsigned long)[inArray count]];
Community
  • 1
  • 1
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • The same with `unsigned long`. So why is the cast necessary? – user102008 Apr 02 '15 at 21:36
  • @user102008 Because `NSUInteger` is platform-dependent. – Sergey Kalinichenko Apr 02 '15 at 23:52
  • So is `unsigned long` – user102008 Apr 03 '15 at 01:25
  • 1
    @user102008 But `unisgned long` matches `%lu` on all systems, while `NSUInteger` may match either `%lu` or `%llu`, depending on the system. – Sergey Kalinichenko Apr 03 '15 at 01:40
  • You said "it is a 32-bit unsigned integer on 32-bit platforms, and a 64-bit unsigned integer on 64-bit platforms". That's the same with `unsigned long`. – user102008 Apr 03 '15 at 02:55
  • 3
    @user102008 It's a coincidence. Sizes of `NSUInteger` and `unsigned long` are controlled by separate entities (the makers of the Cocoa library vs. the makers of the Objective-C compiler). `%lu` is guaranteed to always match `unsigned long`; there is no such guarantee for `NSUInteger`, because it is owned by different people. If Cocoa makers decide tomorrow that `NSUinteger` should be 32 bits on all platform starting with the next release, they can 100% do it. This would break formatting code without the cast. – Sergey Kalinichenko Apr 03 '15 at 10:35
0

You could also try using NSNumber methods:

[NSString stringWithFormat:@"Total Properties: %@", [[NSNumber numberWithUnsignedInteger:[inArray count]] stringValue]];
CyberMoai
  • 496
  • 3
  • 11