NSString属性:复制还是保留?

假设我有一个名为SomeClass的类,其属性名为string:

@interface SomeClass : NSObject
{
NSString* name;
}


@property (nonatomic, retain) NSString* name;


@end

我理解,名称可以被分配为NSMutableString,在这种情况下,这可能会导致错误的行为。

  • 对于一般的字符串,使用copy属性而不是retain是否是一个好主意?
  • “复制”的属性是否比“保留”的属性效率低?
137494 次浏览

复制应该用于NSString。如果它是可变的,那么它就会被复制。如果不是,那么它就会被保留。正是你在应用程序中想要的语义(让类型发挥最好的作用)。

对于类型为符合NSCopying协议的不可变值类的属性,几乎总是应该在@property声明中指定copy。在这种情况下,你几乎不需要指定retain

以下是你想这么做的原因:

NSMutableString *someName = [NSMutableString stringWithString:@"Chris"];


Person *p = [[[Person alloc] init] autorelease];
p.name = someName;


[someName setString:@"Debajit"];

Person.name属性的当前值将根据属性被声明为retaincopy而有所不同——如果属性被标记为retain,则该属性将为@"Debajit",但如果属性被标记为copy,则该属性将为@"Chris"

因为在几乎所有情况下,你都希望防止在背后改变对象的属性,你应该将表示它们的属性标记为copy。(如果你自己编写setter而不是使用@synthesize,你应该记得在其中实际使用copy而不是retain。)

由于name是一个(不可变的)NSString,如果你将另一个NSString设置为name,复制或保留没有区别。换句话说,复制的行为就像retain一样,将引用计数增加1。我认为这是对不可变类的自动优化,因为它们是不可变的,不需要克隆。但是当NSMutalbeString mstr被设置为name时,为了保证正确性,mstr的内容将被复制。

当然,在属性声明中添加“copy”与使用面向对象环境(堆上的对象是通过引用传递的)相违背——这样做的好处之一是,当更改一个对象时,对该对象的所有引用都会看到最新的更改。许多语言提供'ref'或类似的关键字,以允许值类型(即堆栈上的结构)从相同的行为中受益。就我个人而言,我会谨慎地使用copy,如果我觉得一个属性值应该受到保护,不受被赋值对象所做的更改的影响,我可以在赋值过程中调用该对象的copy方法,例如:

p.name = [someName copy];

当然,在设计包含该属性的对象时,只有你知道是否从赋值获取副本的模式中受益- Cocoawithlove.com有以下内容:

“当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可能更合适。

对于一般的字符串,使用copy属性而不是retain属性总是一个好主意吗?

是的——通常总是使用copy属性。

这是因为你的< < em > NSString属性/ em >可以传递一个< em > < / em > NSString实例< em > < / em > NSMutableString实例,因此我们不能真正确定传递的值是一个不可变对象还是可变对象。

“复制”的属性是否比“保留”的属性效率低?

  • 如果你的属性被传递一个< em > < / em > NSString实例,答案是"没有" -复制并不比保留更有效 (它的效率并不低,因为NSString足够聪明,不实际执行复制。)

  • 如果你的属性被传递了一个< em > < / em > NSMutableString实例,那么答案是"是的" -复制比保留效率低 (它的效率较低,因为实际的内存分配和复制必须发生,但这可能是可取的事情。)

  • 一般来说,“被复制”的属性可能效率较低——然而,通过使用NSCopying协议,可以实现一个复制和保留“一样高效”的类。NSString instances就是一个例子。

一般(不只是NSString),什么时候我应该使用“复制”而不是“保留”?

当你不希望属性的内部状态在没有警告的情况下改变时,你应该总是使用copy。即使对于不可变对象,正确编写的不可变对象也可以有效地处理复制(参见下一节关于不可变性和NSCopying)。

retain对象可能有性能方面的原因,但它带来了维护开销——你必须管理内部状态在代码外部改变的可能性。正如他们所说,最后优化。

但是,我把我的类写成了不可变的——难道我不能“保留”它吗?

不使用copy。如果你的类真的是不可变的,那么最好的做法是实现NSCopying协议,让你的类在使用copy时返回自己。如果你这样做:

  • 类的其他用户在使用copy时将获得性能优势。
  • copy注释使你自己的代码更易于维护——copy注释表明你真的不需要担心这个对象在其他地方改变状态。

通过这个例子,复制和保留可以这样解释:

NSMutableString *someName = [NSMutableString stringWithString:@"Chris"];


Person *p = [[[Person alloc] init] autorelease];
p.name = someName;


[someName setString:@"Debajit"];

如果属性的类型是copy,则,

将为[Person name]字符串创建一个新的副本,该副本将保存someName字符串的内容。现在对someName字符串的任何操作都不会对[Person name]产生影响。

[Person name]someName字符串将有不同的内存地址。

但是在保留的情况下,

[Person name]和someename字符串都拥有相同的内存地址,只是someename字符串的保留计数将加1。

因此,someename字符串中的任何变化都将反映在[Person name]字符串中。

我试着遵循这个简单的规则:

  • 当我将分配给我的属性时,我想要保留对象吗?使用<强>复制> < /强。

  • 我想坚持对象我不关心它的内部值目前是或将在未来?使用strong(保留)。

为了说明:我想要保留的名字“Lisa Miller”(复制)还是我想要保留 Lisa Miller (强大的)?她的名字以后可能会改成“丽莎·史密斯”,但她还是同一个人。

@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

你应该一直使用复制来声明NSString属性

@property (nonatomic, copy) NSString* name;

你应该阅读这些信息,以了解它是否返回不可变字符串(以防传递可变字符串)或返回保留字符串(以防传递不可变字符串)

NSCopying Protocol Reference . NSCopying Protocol Reference

实现NSCopying通过保留原始的而不是创建一个 当类及其内容是不可变的

值对象 . 0

对于我们的不可变版本,我们可以这样做:

- (id)copyWithZone:(NSZone *)zone
{
return self;
}

如果字符串非常大,那么复制将影响性能,两个大字符串的副本将使用更多的内存。