字符串常量和字符串字面值有什么区别?

62
我正在学习Objective-C和Cocoa,并遇到了以下声明:
Cocoa框架希望使用全局字符串常量作为字典键、通知和异常名称,以及一些需要字符串作为参数的方法。
我只使用过高级语言,所以从未考虑过字符串的细节。字符串常量和字符串字面值之间有什么区别?
3个回答

87
在Objective-C中,语法@"foo"是一个不可变的字面量字符串实例,它并不像Mike认为的那样从字符串字面量创建一个常量字符串。
Objective-C编译器通常会在编译单元内部合并使用相同字面量字符串的多个实例——这意味着链接器可以在直接链接到单个二进制文件的编译单元之间进行额外的合并。由于Cocoa区分可变和不可变字符串,并且字面量字符串始终也是不可变的,因此这可以很简单和安全地实现。
另一方面,常量字符串通常使用以下语法声明和定义:
// 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执行时可能需要进行许多逐字节的字符串比较以确定谁对此感兴趣。如果由于要比较的字符串具有相同的指针而使得这些比较大部分被短路,那将会获得很大的优势。


1
Mike是正确的。来自书籍《Objective-C 2.0编程》- 字符串@"Programming in Objective-C is fun.\n"是一个常量字符字符串对象的示例。 - Josh Brown
6
《Objective-C 2.0编程》不是该语言的参考手册,将其引用为参考手册是不正确的。在C语言中,“foo”被称为字符串字面量。通常,“常量”指的是指针或变量,而不是字面量;按照定义,字面量是常量。 - Chris Hanson
2
灯泡亮了!!当你想要枚举的效率但不想要数字值时,常量字符串非常有用。很棒的答案,非常有帮助!! - bentford

12

一些定义

字面量(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";

3

由于我完全不懂Objective C,因此我们将使用C++。

如果您将字符串存储到常量变量中:

const std::string mystring = "my string";

现在当您调用方法时,使用my_string,您正在使用一个字符串常量:
someMethod(mystring);

或者,您可以直接使用字符串文字调用这些方法:

someMethod("my string");

他们鼓励使用字符串常量的原因可能是因为Objective C不做“interning”; 也就是说,当您在多个地方使用相同的字符串文字时,它实际上是一个指向单独字符串副本的不同指针。

对于字典键来说,这将产生巨大的影响,因为如果我可以看到两个指针指向相同的内容,那么比起要执行整个字符串比较以确保字符串具有相等的值,这将更加便宜。

编辑: Mike,在C#中,字符串是不可变的,并且具有相同值的文字字符串都会指向同一个字符串值。我想其他具有不可变字符串的语言也是如此。在Ruby中,它具有可变字符串,他们提供了一种新的数据类型:符号(“foo” vs. : foo,前者是可变字符串,后者是不可变标识符,通常用于哈希键)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接