When I need a string-constant, I
#define it, instead of doing the “right” thing and using an
extern const NSString * variable.
UPDATE 2010-07-20Thanks to Elfred Pagen for pointing out that you should always put () around your macros. Wrong:
#define A_STRING @"hello"
instead use (), even when you don’t think you have to:
#define A_STRING (@"hello")
This prevents accidental string concatenation. In C, string-literals separated only by whitespace are implicitly concatenated. It’s the same with Objective-C string literals. This feature lets you break long strings up into several lines, so
NSString *x = @"A long string!" can be rewritten:
NSString *x = @"A long" @" string!";
Unfortunately, this seldom-used feature can backfire in unexpected ways. Consider making an array of two strings:
#define X @"ex" #define P @"plain" a = [NSArray arrayWithObjects:X P, nil];
That looks right, but I forgot a “
X, so after string-concatenation,
Moral of the story: you can never have too many ()’s in macros.
And, now, back to why I use
It’s less code
extern variable means declaring it in a header, and defining it in some implementation file. But a macro is just one line in a header.
It’s faster to lookup
Because there’s only the definition of a macro, Open Quickly/command-double-clicking a macro always jumps to the definition, so you can see what it’s value is in one step. Generally Xcode jumps to a symbol’s declaration first, and then it’s definition, making it slower to lookup the value of a
It’s still type safe
@"NSString literal" has type information, so mistakes like,
#define X (@"immutable string") NSMutableString *y = X; [y appendString:@"z"];
still generate warnings.
It lets the compiler check format-strings
Xcode can catch errors like “
[NSString stringWithFormat:@"reading garbage since there's no argument: %s"]“, if you let it. Unfortunately, the Objective-C compiler isn’t smart enough to check
[NSString stringWithFormat:externConstString,x,y,z]; because it doesn’t know what an
extern variable contains until link-time. But preprocessor macros are evaluated early enough in the build process that that the compiler can check their values.
It can’t be changed at runtime
It’s possible to change the value of
const variables through pointers, like so:
const NSString* const s = @"initial"; NSString **hack = &s; *hack = @"changed!"; NSLog(s);//prints "changed!"
Yes this is pathological code, but I’ve seen it happen (I’m looking at you
Of course, you can re-
#define a preprocessor-symbol, so macros aren’t a panacea for pathological constant-changing code. (Nothing is!) But they push the pathology into compile time, and common wisdom is that it’s easier to debug compile-time problems, so that’s a Good Thing. You may disagree there, and you may be right! All I can say for sure is that in my experience, I’ve had bugs from
const values changing at runtime, but no bugs from re-
#define-ed constants (yet).
Preprocessor macros are damnably dangerous in C. Generally you should avoid them. But for
NSString* constants in applications, I think they’re easier, and arguably less error prone. So go ahead and
#define YOUR_STRING_CONSTANTS (@"like this").