Here’s is quick tip that may save you some debugging time – when using methods that accept format strings, think carefully through the code, paying close attention to both the format string and any relevant argument(s).
Let’s look at an example using NSLog – In the code below I’ve created an NSString object which has text and characters typically used in format strings (“%@”):
// String has the format characters NSString *str = @"Testing %@"; // This can result in EXC_BAD_ACCESS NSLog(str);
Because NSLog expects a format string and a list of arguments, the code above will, in the best case, generate erroneous output, in the worst case, result in an app crash.
The code above is equivalent to the following:
Given there is no parameter for the %@ argument, there will be a random value grabbed from the stack. Beyond the questionable output, there is also a security consideration when writing C code that doesn’t properly manage format string parameters, read Uncontrolled format string, for more information.
The obviously correct way to write the code, regardless of whether or not the string may contain format characters, is below:
NSString *str = @"Testing %@"; NSLog(@"The value of str is: %@", str);
The output will now be as expected: The value of str is: Testing %@
This may seems like an obvious thing to point out, however, given NSLog is often helpful as a simple debugging tool to track the flow through an application:
NSLog(@"Made it to step 1"); ... NSLog(@"Made it to step 2"); ...
passing parameters in a format string is not always required. In addition, if a string you plan to output using NSLog is populated from a remote source or entered by a user, you may not know the contents ahead of time.
Depending on which version of Xcode you are using, you may get a heads up something is amiss during the build process. For example, I am using Xcode 4.2 and the compiler generates the following warning.
To keep things real obvious, I ask the compiler to treat any warnings as errors, meaning the code example above will generate a compile time error rather than simply flag the potential problem. Below is the build setting I tend to use:
Settings such as this are a personal preference, however, in the long run I want my code to always compile clean (read, with no warnings), so I’d rather deal with this up front – and believe me, simple things such as this may save you time in the long run versus tracking down an intermittent runtime crash.