string常量和string常量之间有什么区别?

我正在学Objective-C和Cocoa,并且遇到了这样的说法:

Cocoa框架希望全局string常量而不是string文字用于字典键,通知和exception名称,以及一些采用string的方法参数。

我只用更高级的语言工作,所以从来不必考虑string的细节。 string常量和string常量之间有什么区别?

在Objective-C中,语法@"foo"NSString不可变的 文字实例。 它不会像迈克尔假设的那样从string文字中创build一个常量string。

Objective-C编译器通常在编译单元内部实现字面string – 也就是说,它们合并了同一个string的多个用途 – 并且链接器可以在编译单元之间执行额外的实际操作,直接链接到一个二进制文件中。 (因为Cocoa区分可变string和不可变string,并且string总是也是不变的,所以这可以是简单明了的。)

另一方面, 常量string通常使用如下语法来声明和定义:

 // MyExample.h - declaration, other code references this extern NSString * const MyExampleNotification; // MyExample.m - definition, compiled for other code to reference NSString * const MyExampleNotification = @"MyExampleNotification"; 

这里的语法练习的要点是, 即使跨同一地址空间中的多个框架 (共享库),也可以确保只使用该string的一个实例,从而有效地使用string。 ( const关键字的位置很重要;它保证了指针本身保证不变。)

虽然刻录内存并不像使用8MB内存的25MHz 68030工作站那样大,但要比较string的平等性还是需要时间的。 确保大部分时间都是相等的string也是指针相等的帮助。

比方说,你想通过名字订阅一个对象的通知。 如果您使用非常量string作为名称,发布通知的NSNotificationCenter可能会在确定谁对其感兴趣时进行大量的逐字节string比较。 如果这些比较中的大多数是短路的,因为被比较的string具有相同的指针,那可能是一个很大的胜利。

一些定义

一个文字是一个价值,根据定义是不可改变的。 例如: 10
常量是只读variables或指针。 例如: const int age = 10;
string文字是像“ @""这样的expression式。 编译器将用NSString一个实例replace它。
string常量是指向NSString的只读指针。 例如: NSString *const name = @"John";

最后一行的一些意见:

  • 这是一个常量指针,而不是一个常量对象1objc_sendMsg 2不关心你是否用const限定对象。 如果你想要一个不可变的对象,你必须在对象内部编写这个不变性3
  • All @""expression式确实是不可变的。 它们在编译时被replace为NSConstantString实例,它是NSString一个特殊的子类,具有固定的内存布局5 。 这也解释了为什么NSString是唯一可以在编译时初始化的对象6

一个常量stringconst 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 NSMutableArray *array = [NSMutableArray new]; [array removeAllObjects]; const不会阻止最后一条语句。

4重写expression式的LLVM代码是RewriteModernObjC::RewriteObjCStringLiteral

5要查看NSConstantString定义,请在Xcode中单击它。

6为其他类创build编译时常量将是很容易的,但是这需要编译器使用一个特定的子类。 这将破坏与早期的Objective-C版本的兼容性。


回到你的报价

Cocoa框架希望全局string常量而不是string文本用于字典键,通知和exception名称以及一些采用string的方法参数。 当你有select的时候,你应该总是比string常量更喜欢string常量。 通过使用string常量,您可以获得编译器的帮助来检查拼写,从而避免运行时错误。

它说文字是容易出错的。 但是并不是说他们也慢了点。 比较:

 // string literal [dic objectForKey:@"a"]; // string constant NSString *const a = @"a"; [dic objectForKey:a]; 

在第二种情况下,我使用的键与const指针,所以而不是[a isEqualToString:b] ,我可以做(a==b)isEqualToString:的实现isEqualToString:比较哈希,然后运行C函数strcmp ,因此比直接比较指针要慢。 这就是为什么常量string更好:比较快,错误less。

如果你还希望你的常量string是全局的,就像这样做:

 // header extern NSString *const name; // implementation NSString *const name = @"john"; 

我们使用C ++,因为我的Objective C是完全不存在的。

如果你把一个string存入一个常量variables中:

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

现在,当你调用方法时,你使用my_string,你正在使用一个string常量:

 someMethod(mystring); 

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

 someMethod("my string"); 

原因可能是,他们鼓励你使用string常量是因为Objective C不会“实习”。 也就是说,当你在几个地方使用相同的string时,它实际上是一个指向string单独副本的不同指针。

对于字典键来说,这是非常不同的,因为如果我能看到两个指针指向相同的东西,那么要比string进行比较要便宜得多,以确保string具有相同的值。

编辑:迈克尔,在C#中的string是不可变的,具有相同值的文字string都指向相同的string值。 我想对于其他语言也是如此,它们具有不可变的string。 在具有可变string的Ruby中,他们提供了一个新的数据types:symbols(“foo”vs. foo,前者是一个可变string,后者是一个常用于Hash键的不可变标识符)。