Format String Issue Using NSInteger

The version of Clang supplied with Xcode has over time added a number of additional warnings for potentially unsafe code. This is generally a good thing but I was a little alarmed recently when I opened a project and found the unit test classes had dozens of warnings:

Format String Issue Values of type ‘NSInteger’ should not be used as format arguments; add an explicit cast to ‘long’ instead

The warnings were all for printf format specifiers used in many of the unit test assert statements. A typical example where both expectedCount and count were of type NSInteger:

XCTAssertTrue(expectedCount==count, @"Expected %d got %d", expectedCount, count);

Integer Sizes

The release by Apple of iOS 7 and the A7 processor also introduced a 64-bit runtime to iOS for the first time. The 64-Bit Transition Guide for Cocoa Touch provides guidelines on how to convert your app to a 64-bit binary. Relevant to my problem it also summarises the changes to the sizes of a number of the built-in data types:

The cause of the warning in this case comes from the use of a %d in the format specifier which expects a signed 32-bit integer. However the value supplied was an NSInteger which will be a 64-bit integer with the 64-bit runtime. The fix in this case is to explicitly specify a long integer in the format string (%ld) and cast the NSInteger to a long:

XCTAssertTrue(expectedCount==count, @"Expected %ld got %ld", (long)expectedCount, (long)count);

In the case of an NSUInteger you would use %lu and (unsigned long).

Alternatives to Casting (Updated 14 April 2014)

A couple of interesting alternatives for your consideration if you don’t like casting to a long.

Length Modifiers

This Stack Overflow answer was brought to my attention by Johannes Stühler on Twitter. It makes use of the z and t length modifiers described as follows in the String Programming Guide:

z - Length modifier specifying that a following d, o, u, x, or X conversion specifier applies to a size_t or the corresponding signed integer type argument.

t - Length modifier specifying that a following d, o, u, x, or X conversion specifier applies to a ptrdiff_t or the corresponding unsigned integer type argument.

This allows us to write the following and have it be correct for both 32-bit and 64-bit runtimes:

XCTAssertTrue(expectedCount==count, @"Expected %zd got %zd", expectedCount, count);

The equivalent for an NSUInteger would use %tu.

NSNumber Literals

Another alternative, which I think I saw first on the Big Nerd Ranch blog is to use the Objective-C literal syntax to convert to an NSNumber object:

XCTAssertTrue(expectedCount==count, @"Expected %@ got %@", @(expectedCount), @(count));

Fix All In Scope

It may also we be worth remembering that if you have a number of such warnings you can get Xcode to fix them all in one go using the “Fix All In Scope” option from the “Editor” menu (keyboard shortcut is ⌃⌥⌘F).

Never Miss A Post

Sign up to get my iOS posts and news direct to your inbox and I'll also send you my free iOS Size Classes Cheat Sheet

    Unsubscribe at any time. See Privacy Policy.

    Archives Categories