Some definitions
A literal is a value, which is immutable by definition. eg: 10
A constant is a read-only variable or pointer. eg: const int age = 10;
A string literal is a expression like @""
. The compiler will replace this with an instance of NSString
.
A string constant is a read-only pointer to NSString
. eg: NSString *const name = @"John";
Some comments on the last line:
- That's a constant pointer, not a constant object1.
objc_sendMsg
2 doesn't care if you qualify the object with const
. If you want an immutable object, you have to code that immutability inside the object3.
- All
@""
expressions are indeed immutable. They are replaced4 at compile time with instances of NSConstantString
, which is a specialized subclass of NSString
with a fixed memory layout5. This also explains why NSString
is the only object that can be initialized at compile time6.
A constant string would be const NSString* name = @"John";
which is equivalent to NSString const* name= @"John";
. Here, both syntax and programmer intention are wrong: const <object>
is ignored, and the NSString
instance (NSConstantString
) was already immutable.
1 The keyword const
applies applies to whatever is immediately to its left. If there is nothing to its left, it applies to whatever is immediately to its right.
2 This is the function that the runtime uses to send all messages in Objective-C, and therefore what you can use to change the state of an object.
3 Example: in const NSMutableArray *array = [NSMutableArray new]; [array removeAllObjects];
const doesn't prevent the last statement.
4 The LLVM code that rewrites the expression is RewriteModernObjC::RewriteObjCStringLiteral
in RewriteModernObjC.cpp.
5 To see the NSConstantString
definition, cmd+click it in Xcode.
6 Creating compile time constants for other classes would be easy but it would require the compiler to use a specialized subclass. This would break compatibility with older Objective-C versions.
Back to your quote
The Cocoa frameworks expect that global string constants rather than
string literals are used for dictionary keys, notification and
exception names, and some method parameters that take strings. You
should always prefer string constants over string literals when you
have a choice. By using string constants, you enlist the help of the
compiler to check your spelling and thus avoid runtime errors.
It says that literals are error prone. But it doesn't say that they are also slower. Compare:
// string literal
[dic objectForKey:@"a"];
// string constant
NSString *const a = @"a";
[dic objectForKey:a];
In the second case I'm using keys with const pointers, so instead [a isEqualToString:b]
, I can do (a==b)
. The implementation of isEqualToString:
compares the hash and then runs the C function strcmp
, so it is slower than comparing the pointers directly. Which is why constant strings are better: they are faster to compare and less prone to errors.
If you also want your constant string to be global, do it like this:
// header
extern NSString *const name;
// implementation
NSString *const name = @"john";