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";
最后一行的一些意见:
- 这是一个常量指针,而不是一个常量对象1 。
objc_sendMsg
2不关心你是否用const
限定对象。 如果你想要一个不可变的对象,你必须在对象内部编写这个不变性3 。 - All
@""
expression式确实是不可变的。 它们在编译时被replace为NSConstantString
实例,它是NSString
一个特殊的子类,具有固定的内存布局5 。 这也解释了为什么NSString
是唯一可以在编译时初始化的对象6 。
一个常量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 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键的不可变标识符)。