Cocoa框架希望使用全局字符串常量作为字典键、通知和异常名称,以及一些需要字符串作为参数的方法。
我只使用过高级语言,所以从未考虑过字符串的细节。字符串常量和字符串字面值之间有什么区别?
@"foo"
是一个不可变的、字面量字符串实例,它并不像Mike认为的那样从字符串字面量创建一个常量字符串。// MyExample.h - declaration, other code references this
extern NSString * const MyExampleNotification;
// MyExample.m - definition, compiled for other code to reference
NSString * const MyExampleNotification = @"MyExampleNotification";
这里的语法练习意在说明,你可以通过确保同一地址空间中多个框架(共享库)中仅存在一个该字符串实例来使用字符串,从而使其更高效。 (const
关键字的放置位置很重要,它保证指针本身是恒定的。)
虽然在拥有8MB RAM、25MHz 68030工作站的时代,占用内存不再是那么重要,但是比较字符串的相等性可能需要时间。确保大部分情况下相等的字符串也具有相等的指针有助于提高效率。
举个例子,如果您想通过名称订阅对象的通知。如果使用非常量字符串作为名称,则发布通知的NSNotificationCenter
执行时可能需要进行许多逐字节的字符串比较以确定谁对此感兴趣。如果由于要比较的字符串具有相同的指针而使得这些比较大部分被短路,那将会获得很大的优势。
字面量(literal)是一个不可变的值。例如:10
。
常量(constant)是一个只读的变量或指针。例如:const int age = 10;
。
字符串字面量(string literal)是一个表达式,形如@""
。编译器会将其替换为一个NSString
实例。
字符串常量(string constant)是一个指向NSString
的只读指针。例如:NSString *const name = @"John";
。
对于最后一行:
objc_sendMsg
并不关心对象是否被标记为const
。如果你想要一个不可变对象,你必须在对象内部实现不可变性。@""
表达式都是不可变的。它们在编译时被替换为NSConstantString
的实例,它是NSString
的一个专门的子类,具有固定的内存布局。这也解释了为什么NSString
是唯一可以在编译时初始化的对象。常量字符串(constant string)应该是const NSString* name = @"John";
或等价的NSString const* name= @"John";
。在这里,语法和程序员意图都是错误的:const <object>
被忽略了,NSString
实例(NSConstantString
)已经是不可变的。
1 关键字const
应用于其左边的内容。如果左边没有任何内容,则应用于紧挨着的右边的内容。
2 这是Objective-C中运行时用于发送所有消息的函数,因此可以用它来改变对象的状态。
3 示例:在const NSMutableArray *array = [NSMutableArray new]; [array removeAllObjects];
中,const并不能阻止最后一条语句。
4 重写表达式的LLVM代码是RewriteModernObjC::RewriteObjCStringLiteral
。
5 要查看NSConstantString
的定义,请在Xcode中使用cmd+单击。
6 对其他类创建编译时常量很容易,但这需要编译器使用一个专门的子类。这会破坏与旧版本Objective-C的兼容性。
Cocoa框架期望在字典键、通知和异常名称以及一些接受字符串参数的方法中使用全局字符串常量而不是字符串字面量。当有选择时,你应该总是优先使用字符串常量而不是字符串字面量。通过使用字符串常量,你可以获得编译器的帮助来检查拼写,从而避免运行时错误。
它说字面量容易出错,但没有说它们也比较慢。比较一下:
// string literal
[dic objectForKey:@"a"];
// string constant
NSString *const a = @"a";
[dic objectForKey:a];
在第二种情况中,我使用具有const指针的键,所以可以使用(a==b)代替[a isEqualToString:b]。 isEqualToString:
的实现比较哈希值,然后运行C函数strcmp
,因此与直接比较指针相比,它速度较慢。这就是为什么常量字符串更好:它们比较快,且出错的几率更小。// header
extern NSString *const name;
// implementation
NSString *const name = @"john";
由于我完全不懂Objective C,因此我们将使用C++。
如果您将字符串存储到常量变量中:
const std::string mystring = "my string";
someMethod(mystring);
或者,您可以直接使用字符串文字调用这些方法:
someMethod("my string");
对于字典键来说,这将产生巨大的影响,因为如果我可以看到两个指针指向相同的内容,那么比起要执行整个字符串比较以确保字符串具有相等的值,这将更加便宜。
编辑: Mike,在C#中,字符串是不可变的,并且具有相同值的文字字符串都会指向同一个字符串值。我想其他具有不可变字符串的语言也是如此。在Ruby中,它具有可变字符串,他们提供了一种新的数据类型:符号(“foo” vs. : foo,前者是可变字符串,后者是不可变标识符,通常用于哈希键)。