NSString property: copy or retain?
假设我有一个名为
1 2 3 4 5 6 7 8 | @interface SomeClass : NSObject { NSString* name; } @property (nonatomic, retain) NSString* name; @end |
我知道这个名字可能被指定为
- 一般来说,对于字符串,使用
copy 属性而不是retain 属性总是一个好主意吗? - "复制"财产是否比"保留"财产效率低?
对于类型是符合
这就是为什么你想这样做:
1 2 3 4 5 6 | NSMutableString *someName = [NSMutableString stringWithString:@"Chris"]; Person *p = [[[Person alloc] init] autorelease]; p.name = someName; [someName setString:@"Debajit"]; |
因为在几乎所有情况下,您都希望防止对象背后的属性发生变化,所以应该标记表示它们的属性
复制应用于nsstring。如果它是可变的,那么它就会被复制。如果不是的话,那就保留下来。确切地说,是应用程序中需要的语义(让类型做最好的事情)。
For strings in general, is it always a good idea to use the copy attribute instead of retain?
是-通常总是使用copy属性。
这是因为您的nsstring属性可以通过nsstring实例或nsmutableString实例传递,因此我们无法真正确定传递的值是不可变对象还是可变对象。
Is a"copied" property in any way less efficient than such a"retain-ed" property?
如果您的属性正在传递一个nsstring实例,则答案是"否"——复制的效率不低于保留。(它的效率并不低,因为nsstring足够智能,无法实际执行复制。)
如果您的属性被传递给一个nsmutableString实例,那么答案是"是"——复制比保留效率低。(它效率较低,因为必须进行实际的内存分配和复制,但这可能是一件可取的事情。)
一般来说,"复制的"属性可能效率较低,但是通过使用
NSCopying 协议,可以实现一个"与保留一样高效"的类来进行复制。NSstring实例就是这样一个例子。
Generally (not just for NSString), when should I use"copy" instead of"retain"?
当不希望更改属性的内部状态而不需要警告时,应始终使用EDCOX1×1。即使对于不可变的对象,正确写入的不可变对象也将有效地处理复制(参见下一节关于不可变性和EDCOX1(0))。
EDOCX1 3个对象可能有性能上的原因,但是它有一个维护开销——您必须管理内部状态在代码之外变化的可能性。正如他们所说,优化最后。
But, I wrote my class to be immutable - can't I just"retain" it?
不使用EDOCX1×1。如果你的类真的是不可变的,那么最好使用EDCOX1 0协议来让你的类在使用EDCOX1×1时返回。如果你这样做:
- 您班的其他用户在使用
copy 时将获得性能优势。 copy 注释使您自己的代码更易于维护—copy 注释表示您确实不需要担心这个对象在其他地方改变状态。
我试着遵循这个简单的规则:
当我将对象分配给我的属性时,是否要保留对象的值?使用复制品。
我是想抓住这个对象,而不关心它的内部值目前是什么,还是将来会是什么?使用强力胶(保留)。
举例说明:我想保留"丽莎·米勒"这个名字(副本)还是我想保留"丽莎·米勒"这个人(强)?她的名字后来可能改为"丽莎·史密斯",但她仍然是同一个人。
通过这个例子,复制和保留可以解释为:
1 2 3 4 5 6 | NSMutableString *someName = [NSMutableString stringWithString:@"Chris"]; Person *p = [[[Person alloc] init] autorelease]; p.name = someName; [someName setString:@"Debajit"]; |
如果属性是copy类型,则,
将为保存
但如果保留,
因此,somename字符串中的任何更改都将反映在
当然,将"copy"放在属性声明上会很快遇到使用面向对象的环境的情况,在这种环境中,堆中的对象是通过引用传递的——您在这里得到的好处之一是,更改对象时,对该对象的所有引用都会看到最新的更改。许多语言提供"ref"或类似的关键字,以允许值类型(即堆栈上的结构)从相同的行为中受益。就我个人而言,我会谨慎使用copy,如果我觉得应该保护一个属性值,使其不受分配对象更改的影响,我可以在分配期间调用该对象的copy方法,例如:
1 | p.name = [someName copy]; |
当然,在设计包含该属性的对象时,只有您知道设计是否受益于分配复制的模式-cocoawithlove.com有以下内容:
"如果setter参数可能是可变的,但属性的内部状态在没有警告的情况下无法更改,则应使用copy访问器"-因此,判断是否可以承受意外更改的值完全是您自己的决定。想象一下这个场景:
1 2 3 4 5 6 7 8 | //person object has details of an individual you're assigning to a contact list. Contact *contact = [[[Contact alloc] init] autorelease]; contact.name = person.name; //person changes name [[person name] setString:@"new name"]; //now both person.name and contact.name are in sync. |
在这种情况下,如果不使用复制,我们的联系对象会自动获取新值;如果我们确实使用它,我们必须手动确保检测到并同步更改。在这种情况下,保留语义可能是可取的;在另一种情况下,复制可能更合适。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | @interface TTItem : NSObject @property (nonatomic, copy) NSString *name; @end { TTItem *item = [[TTItem alloc] init]; NSString *test1 = [NSString stringWithFormat:@"%d / %@", 1, @"Go go go"]; item.name = test1; NSLog(@"-item.name: point = %p, content = %@; test1 = %p", item.name, item.name, test1); test1 = [NSString stringWithFormat:@"%d / %@", 2, @"Back back back"]; NSLog(@"+item.name: point = %p, content = %@, test1 = %p", item.name, item.name, test1); } Log: -item.name: point = 0x9a805a0, content = 1 / Go go go; test1 = 0x9a805a0 +item.name: point = 0x9a805a0, content = 1 / Go go go, test1 = 0x9a84660 |
您应该一直使用copy来声明nsstring属性
1 | @property (nonatomic, copy) NSString* name; |
您应该阅读这些内容,以获取有关它是返回不可变字符串(在传递可变字符串的情况下)还是返回保留字符串(在传递不可变字符串的情况下)的更多信息。
nscopying协议参考
Implement NSCopying by retaining the original instead of creating a
new copy when the class and its contents are immutable
价值客体
So, for our immutable version, we can just do this:
1 2 3 4 | - (id)copyWithZone:(NSZone *)zone { return self; } |
如果字符串非常大,那么复制将影响性能,并且两个大字符串副本将使用更多内存。
由于名称是一个(不可变的)