views:

216

answers:

4

I've reading in several post and in Apple's code guidelines that in Objective-C String constants should be defined as extern NSString *const MY_CONSTANT; and that the #define directive should be avoided. Why is that? I know that #define is run at precompile time but all string will share the same memory address. The only advantage I read was that if the constant must be updated or changed you don't have to recompile your entire project. So that i s the reason why #define should be avoided?

Thanks

UPDATE: In this case is good to use a #define or there is a better approach?

/* Constants Definition */
#define SERVER_URL @"http://subdomain.domain.edu.ar/Folder/"
NSString *const ServerURL = SERVER_URL;
NSString *const LoginURL = SERVER_URL@"welcome.asp";
NSString *const CommandURL = SERVER_URL@"com.asp";
A: 

As far as I know, #define only lets you define C-style string constants. To create a constant NSString object, you have to declare it in the header, and then give it a value in one of your .m files.

Header file:

extern NSString *MyConstantString;

Main file:

NSString *MyConstantString = @"String value";

Asher Dunn
Why would `#define HELLO @"World"` not work in Objective-C?
Dirk
`#define MyConstantString @"String Value"` doesn't work?
Chuck
#define just does a text replace so I don't see why #define MSG @"Hello" won't work
GuidoMB
`#define HELLO @"World"` does work...
Jasarien
Asher Dunn: #define is a *preprocessor* directive, not part of the C language itself (same ISO standard, but separate language). All it does is create a macro. There is no restriction on what text you can set as the macro's value.
Peter Hosey
+5  A: 

It's not necessarily guaranteed that there will only be one NXConstantString object for a given string literal in an entire application. It seems pretty likely that different compilation units might have different objects for the same constant string. For example, if somebody writes a plugin, one constant string will be generated for occurrences of that NSString literal in the plugin and one will be generated for occurrences in the host application, and these will not be pointer-equal.

Chuck
+2  A: 

A practical reason to use the constant as opposed to the definition is that you can do direct comparisons (using ==) instead of using isEqual:. Consider:

NSString * const kSomeStringConstant = @"LongStringConstantIsLong";
...
[someArray addObject:kSomeStringConstant];
if ([someArray lastObject] == kSomeStringConstant)
{
   ...
}

This would work, since the == comparison would be comparing identical const pointers to a single NSString object. Using #define, however:

#define STRING_CONSTANT @"MacrosCanBeEvil";
...
[SomeArray addObject:STRING_CONSTANT]; // a new const `NSString` is created
if ([someArray lastObject] == STRING_CONSTANT) // and another one, here.
{
    ...
}

This would not work out, since the two strings would have unique pointers. To compare them effectively, you would have to do a character-by-character comparison using isEqual:

if ([[someArray lastObject] isEqual:STRING_CONSTANT])
{
    ...
}

This can be far more costly in terms of execution time than the simple == comparison.

Another motivation could be the size of the executable itself. The #defined constant would actually appear, in place, wherever it was used in the code. This could mean that the string appears many times in your executable. In contrast, the constant should (with modern compilers) be defined only once, and all further usages would make reference to the pointer to that one definition.

Now, before anyone yells at me for premature optimization, consider that the two approaches are almost identical in terms of implementation, but the const pointer method is far superior in terms of code size and execution time.

e.James
I see that Chuck's answer contradicts mine, and I'm not sure whose is correct.
e.James
The constant would not appear "in place" wherever it was used in code. Both ways will generate references to NXConstantString objects. It is true that more may be generated by string literals, but it will almost certainly be a lot less than one object for every string literal in the code.
Chuck
A: 

The best argument I have heard is that const strings show up in the debugger, whereas macros do not.

meeselet