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实例,则答案是“是”——复制的效率低于保留。
        (由于必须进行实际的内存分配和复制,因此效率较低,但这可能是一件理想的事情。)

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

      一般来说(不仅仅是NSString),什么时候应该使用“复制”而不是“保留”? 如果不希望属性的内部状态在没有警告的情况下更改,则应始终使用
      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;
      }