提问



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


@interface SomeClass : NSObject
{
    NSString* name;
}

@property (nonatomic, retain) NSString* name;

@end


我知道这个名字可能会被分配NSMutableString,在这种情况下,这可能会导致错误的行为。



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

  • 复制属性是否比这种保留属性效率低?


最佳参考


对于类型是符合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"]],但@"Chris"如果财产被标记copy


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

其它参考1


复制应该用于NSString。如果它是可变的,那么它就会被复制。如果不是,那么它就会被保留下来。确切地说,你想要在应用程序中使用的语义(让类型做到最好)。

其它参考2



  

对于字符串一般来说,使用copy属性而不是retain?

总是一个好主意


是 - 通常始终使用复制属性。


这是因为 NSString属性 可以传递 NSString实例 NSMutableString实例 ,因此我们无法确定传递的值是不可变对象还是可变对象。



  

复制属性是否比这种保留属性效率低?





  • 如果您的媒体资源是 NSString实例 ,则答案为 - 复制的效率不低于保留。< BR>
    (效率不低,因为NSString非常智能,无法实际执行副本。)

  • 如果您的媒体资源已通过 NSMutableString实例 ,则答案为 - 复制效果低于保留。

    (效率较低,因为必须进行实际的内存分配和复制,但这可能是理想的事情。)

  • 一般来说,复制属性可能效率较低 - 但是通过使用NSCopying协议,可以实现一个同样有效的类来复制它是保留。 NSString实例 就是一个例子。




  

一般情况下(不只是NSString),何时应该使用copy而不是retain?




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


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



  

但是,我写的课程是不可变的 - 我不能保留它吗?




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



  • 班级的其他用户在使用copy时会获得性能优势。

  • copy注释使您自己的代码更易于维护 - copy注释表明您真的不需要担心这个对象在其他地方改变状态。


其它参考3


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



  • 我是否希望在我将属性分配给我的属性的时间点 ?使用复制

  • 我是否希望坚持对象我不关心其内部价值目前或将来会是什么?使用(保留)。



举例说明:我是否想要坚持名称Lisa Miller( copy )或者我想要坚持丽莎米勒(强大)?她的名字可能会改为丽莎史密斯,但她仍然是同一个人。

其它参考4


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


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

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

[someName setString:@"Debajit"];


如果属性是复制类型,那么,


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


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


但是如果保留,


[Person name]将保存与somename字符串相同的内存地址,只是somename字符串的保留计数将增加1。


因此,somename字符串中的任何更改都将反映在[Person name]字符串中。

其它参考5


当然,将复制放在属性声明上面对使用面向对象的环境,其中堆上的对象通过引用传递 - 这里得到的一个好处是,在更改对象时,对该对象的所有引用看到最新的变化。许多语言提供ref或类似关键字以允许值类型(即堆栈上的结构)受益于相同的行为。就个人而言,我谨慎地使用副本,如果我觉得应保护属性值不受对其所分配对象的更改的影响,我可以在赋值期间调用该对象的复制方法,例如:


p.name = [someName copy];


当然,在设计包含该属性的对象时,只有您才能知道设计是否受益于分配采用副本的模式 - Cocoawithlove.com具有以下内容:[52]


当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.


在这种情况下,不使用复制,我们的联系对象会自动获取新值;但是,如果我们确实使用它,我们必须手动确保检测到并同步了更改。在这种情况下,保留语义可能是可取的;在另一种情况下,复制可能更合适。

其它参考6


@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

其它参考7


您应该始终使用复制来声明NSString属性


@property (nonatomic, copy) NSString* name;


您应该阅读这些以获取有关它是否返回不可变字符串(如果传递了可变字符串)或返回保留字符串(如果传递了不可变字符串)的更多信息


NSCopying协议参考[53]



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



价值对象[54]



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



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

其它参考8


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

其它参考9


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