When to use static string vs. #define

I am a little confused as to when it's best to use:

static NSString *AppQuitGracefullyKey = @"AppQuitGracefully";

instead of

#define AppQuitGracefullyKey    @"AppQuitGracefully"

I've seen questions like this for C or C++, and I think what's different here is that this is specifically for Objective C, utilizing an object, and on a device like the iPhone, there may be stack, code space or memory issues that I don't yet grasp.

One usage would be:

appQuitGracefully =  [[NSUserDefaults standardUserDefaults] integerForKey: AppQuitGracefullyKey];

Or it is just a matter of style?

Thanks.


If you use a static, the compiler will embed exactly one copy of the string in your binary and just pass pointers to that string around, resulting in more compact binaries. If you use a #define, there will be a separate copy of the string stored in the source on each use. Constant string coalescing will handle many of the dups but you're making the linker work harder for no reason.


See "static const" vs "#define" vs "enum". The main advantage of static is type safety.

Other than that, the #define approach introduces a flexibility of inline string concatenation which cannot be done with static variables, e.g.

#define ROOT_PATH @"/System/Library/Frameworks"
[[NSBundle bundleWithPath:ROOT_PATH@"/UIKit.framework"] load];

but this is probably not a good style :).


I actually would recommend neither, you should use extern instead. Objective-c already defines FOUNDATION_EXPORT which is more portable than extern, so a global NSString instance would look something like this:

.h

FOUNDATION_EXPORT NSString * const AppQuitGracefullyKey;

.m

NSString * const AppQuitGracefullyKey = @"AppQuitGracefully";

I usually put these in declaration files (such as MyProjectDecl.h) and import whenever I need.

There are a few differences to these approaches:

  • #define has several downsides, such as not being type safe. It is true that there are workarounds for that (such as #define ((int)1)) but what's the point? And besides, there are debugging disadvantages to that approach. Compilers prefer constants. See this discussion.
  • static globals are visible in the file they are declared.
  • extern makes the variable visible to all files. That contrasts with static.

Static and extern differ in visibility. It's also notable that neither of these approaches duplicates the string (not even #define) as the compiler uses String Interning to prevent that. In this NSHipster post they show proof:

NSString *a = @"Hello";
NSString *b = @"Hello";
BOOL wtf = (a == b); // YES

The operator == returns YES only if the two variables point at the same instance. And as you can see, it does.

The conclusion is: use FOUNDATION_EXPORT for global constants. It's debug friendly and will be visible allover your project.


After doing some search (this question/answer among other things) I think it is important to say that anytime when you are using string literal @"AppQuitGracefully" constant string is created, and no matter how many times you use it it will point to the same object.

So I think (and I apologize me if I'm wrong) that this sentence in above answer is wrong: If you use a #define, there will be a separate copy of the string stored in the source on each use.