我正在学习Objective-C和Cocoa并且遇到过这样的陈述:
Cocoa框架期望全局字符串常量而不是字符串文字用于字典键,通知和异常名称,以及一些带字符串的方法参数。
我只使用更高级别的语言,所以从来没有必要考虑字符串的细节。字符串常量和字符串文字之间有什么区别?
答案 0 :(得分:83)
在Objective-C中,语法@"foo"
是NSString
的不可变,文字实例。它没有像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 :(得分:12)
literal 是一个值,根据定义是不可变的。例如:10
常量是只读变量或指针。例如:const int age = 10;
字符串文字是一个类似@""
的表达式。编译器将用NSString
的实例替换它
字符串常量是指向NSString
的只读指针。例如:NSString *const name = @"John";
关于最后一行的一些评论:
objc_sendMsg
2 并不关心您是否使用const
限定对象。如果你想要一个不可变对象,你必须在对象 3 中编写不可变性。 @""
表达式确实是不可变的。它们在编译时用NSConstantString
的实例替换为 4 ,NSString
是NSString
的专用子类,具有固定的内存布局 5 。这也解释了为什么const NSString* name = @"John";
是唯一可以在编译时 6 初始化的对象。 常量字符串将为NSString const* name= @"John";
,相当于const <object>
。这里,语法和程序员的意图都是错误的:NSString
被忽略,NSConstantString
实例(const
)已经是不可变的。
1 关键字const NSMutableArray *array = [NSMutableArray new]; [array removeAllObjects];
适用于其左侧的任何内容。如果左边没有任何内容,则适用于其右边的任何内容。
2 这是运行时用来发送Objective-C中所有消息的函数,因此可用于更改对象状态的函数。 子>
3 示例:在RewriteModernObjC::RewriteObjCStringLiteral
const中不会阻止最后一个语句。
4 在RewriteModernObjC.cpp中重写表达式的LLVM代码为NSConstantString
。
5 要查看// string literal
[dic objectForKey:@"a"];
// string constant
NSString *const a = @"a";
[dic objectForKey:a];
定义,cmd +在Xcode中单击它。
6 为其他类创建编译时常量很容易,但需要编译器使用专门的子类。这会破坏与旧版Objective-C版本的兼容性。
Cocoa框架期望全局字符串常量而不是 字符串文字用于字典键,通知和 异常名称,以及一些带字符串的方法参数。您 你应该总是喜欢字符串常量而不是字符串文字 有选择。通过使用字符串常量,您可以获得帮助 编译器检查你的拼写,从而避免运行时错误。
它说文字很容易出错。但它并没有说它们也慢。比较:
[a isEqualToString:b]
在第二种情况下,我使用带有const指针的键,而(a==b)
代替我isEqualToString:
。 strcmp
的实现比较哈希然后运行C函数// header
extern NSString *const name;
// implementation
NSString *const name = @"john";
,因此它比直接比较指针慢。 为什么常量字符串更好:它们比较快,不易出错。
如果您还希望常量字符串是全局的,请按以下步骤操作:
{{1}}
答案 2 :(得分:3)
让我们使用C ++,因为我的目标C完全不存在。
如果将字符串存入常量变量:
const std::string mystring = "my string";
现在当你调用方法时,使用my_string,你正在使用一个字符串常量:
someMethod(mystring);
或者,您可以直接使用字符串文字调用这些方法:
someMethod("my string");
大概是,他们鼓励你使用字符串常量的原因是因为Objective C没有“实习”;也就是说,当你在几个地方使用相同的字符串文字时,它实际上是一个指向字符串单独副本的不同指针。
对于字典键,这会产生很大的不同,因为如果我能看到两个指针指向同一个东西,那就比必须进行整个字符串比较要便宜得多,以确保字符串具有相同的值。
编辑: Mike,C#字符串是不可变的,具有相同值的文字字符串都指向相同的字符串值。我想其他语言也是如此,它们具有不可变的字符串。在具有可变字符串的Ruby中,它们提供了一种新的数据类型:符号(“foo”与:foo,前者是可变字符串,后者是常用于Hash键的不可变标识符)。