Objective c NSString属性:复制还是保留?
假设我有一个名为Objective c NSString属性:复制还是保留?,objective-c,cocoa,cocoa-touch,Objective C,Cocoa,Cocoa Touch,假设我有一个名为SomeClass的类,带有字符串属性名: @interface SomeClass : NSObject { NSString* name; } @property (nonatomic, retain) NSString* name; @end 我知道名称可能会被分配一个NSMutableString,在这种情况下,这可能会导致错误行为 对于一般的字符串,使用copy属性而不是retain总是一个好主意吗 “复制”财产是否比“保留”财产的效率低 复制应用于NS
SomeClass
的类,带有字符串
属性名:
@interface SomeClass : NSObject
{
NSString* name;
}
@property (nonatomic, retain) NSString* name;
@end
我知道名称可能会被分配一个NSMutableString
,在这种情况下,这可能会导致错误行为
- 对于一般的字符串,使用
属性而不是copy
总是一个好主意吗李>retain
- “复制”财产是否比“保留”财产的效率低
- 复制应用于NSString。如果它是可变的,那么它会被复制。如果不是,那么它只是被保留。确切地说,您希望在应用程序中使用的语义(让类型发挥最大作用)。对于类型为符合
NSCopying
协议的不可变值类的属性,您几乎总是应该在@property
声明中指定copy
。在这种情况下,指定retain
几乎是你永远不会想要的
以下是您希望这样做的原因:
NSMutableString *someName = [NSMutableString stringWithString:@"Chris"];
Person *p = [[[Person alloc] init] autorelease];
p.name = someName;
[someName setString:@"Debajit"];
Person.name
属性的当前值将不同,具体取决于该属性是声明为retain
还是copy
——如果该属性被标记为retain
,那么它将是@“Debajit”
,但是如果该属性被标记为copy
,那么它将是@“Chris”
因为在几乎所有情况下,您都希望防止对象的属性在背后发生变化,所以应该标记表示它们的属性
copy
。(如果您自己编写setter而不是使用@synthetic
,您应该记住实际使用copy
而不是retain
。由于名称是一个(不可变的)NSString
,如果您将另一个NSString
设置为名称,则复制或保留没有任何区别。换句话说,copy的行为就像retain一样,将引用计数增加1。我认为这是对不可变类的自动优化,因为它们是不可变的,不需要克隆。但是当NSMutalbeString
mstr
设置为name时,为了正确起见,mstr
的内容将被复制。当然,在属性声明中添加“copy”是对使用面向对象环境的一种挑战,在这种环境中,堆上的对象是通过引用传递的——这里的好处之一是,在更改对象时,对该对象的所有引用都会看到最新的更改。许多语言提供“ref”或类似的关键字,以允许值类型(即堆栈上的结构)受益于相同的行为。就我个人而言,我会尽量少用copy,如果我觉得应该保护属性值不受对其赋值对象所做更改的影响,我可以在赋值过程中调用该对象的copy方法,例如:
p.name = [someName copy];
当然,在设计包含该属性的对象时,只有您才能知道设计是否受益于工作分配复制的模式,如下所示:
“当setter参数可能是可变的,但不能在没有警告的情况下更改属性的内部状态时,应使用复制访问器”-因此,关于是否可以承受意外更改的值的判断完全由您自己决定。想象一下这个场景:
//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.
在这种情况下,不使用copy,我们的contact对象自动获取新值;但是,如果我们确实使用了它,我们必须手动确保检测到并同步了更改。在这种情况下,可能需要保留语义;另一方面,复制可能更合适
对于一般的字符串,使用copy属性而不是retain总是一个好主意吗?
是-通常总是使用“复制”属性。
这是因为您的NSString属性可以传递一个NSString实例或NSMutableString实例,因此我们无法真正确定传递的值是不可变的还是可变的对象
“复制”财产是否比“保留”财产的效率低?
- 如果向您的属性传递了一个NSString实例,答案是“否”——复制的效率不低于保留。
(它的效率并没有降低,因为NSString足够智能,不会实际执行复制。) - 如果向您的属性传递了一个NSMutableString实例,则答案是“是”——复制的效率低于保留。
(由于必须进行实际的内存分配和复制,因此效率较低,但这可能是一件理想的事情。) - 一般来说,“复制的”属性可能效率较低-但是通过使用
协议,可以实现复制和保留“同样有效”的类NSString实例就是一个例子NSCopying
copy
。即使对于不可变对象,正确编写的不可变对象也能有效地处理拷贝(请参阅下一节关于不可变性和NSCopying
)
保留对象可能有性能方面的原因,但这会带来维护开销-您必须管理内部状态在代码外部发生变化的可能性。正如他们所说,优化是最后一步
但是,我编写的类是不可变的——我不能“保留”它吗?
否-使用复制。如果您的类确实是不可变的,那么最好实现NSCopying
协议,以便在使用copy
时使您的类返回自身。如果您这样做:
- 类的其他用户在使用
NSMutableString *someName = [NSMutableString stringWithString:@"Chris"];
Person *p = [[[Person alloc] init] autorelease];
p.name = someName;
[someName setString:@"Debajit"];
@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
@property (nonatomic, copy) NSString* name;
- (id)copyWithZone:(NSZone *)zone
{
return self;
}