代码之家  ›  专栏  ›  技术社区  ›  Debajit

nsstring属性:复制还是保留?

  •  325
  • Debajit  · 技术社区  · 16 年前

    假设我有一个叫 SomeClass 用一个 string 属性名称:

    @interface SomeClass : NSObject
    {
        NSString* name;
    }
    
    @property (nonatomic, retain) NSString* name;
    
    @end
    

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

    • 一般来说,是不是 总是 使用 copy 属性而不是 retain ?
    • “复制”财产是否比“保留”财产效率低?
    10 回复  |  直到 10 年前
        1
  •  436
  •   Soner Gönül to StackOverflow    13 年前

    对于类型为不可变值类且符合 NSCopying 协议,您几乎总是应该指定 copy 在你 @property 宣言。指定 retain 在这种情况下,你几乎不想得到。

    这就是为什么你想这样做:

    NSMutableString *someName = [NSMutableString stringWithString:@"Chris"];
    
    Person *p = [[[Person alloc] init] autorelease];
    p.name = someName;
    
    [someName setString:@"Debajit"];
    

    的当前值 Person.name 属性将根据是否声明该属性而不同 保持 复制 _ @"Debajit" 如果属性已标记 保持 但是 @"Chris" 如果属性已标记 复制 .

    因为在几乎所有情况下你都想 防止 改变一个对象背后的属性,您应该标记表示它们的属性。 复制 . (如果你自己写了setter而不是用 @synthesize 你应该记住实际使用 复制 而不是 保持 在里面)

        2
  •  120
  •   Frank Krueger    16 年前

    拷贝 应用于nsstring。如果它是可变的,那么它就会被复制。如果不是的话,那就保留下来。确切地说,是应用程序中需要的语义(让类型做最好的事情)。

        3
  •  65
  •   TJez    13 年前

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

    是-通常总是使用copy属性。

    这是因为你 nsstring属性 可以通过 nsstring实例 nsmutableString实例 因此,我们不能真正确定要传递的值是不可变对象还是可变对象。

    “复制”财产是否比“保留”财产效率低?

    • 如果您的财产通过 nsstring实例 答案是” “-复制的效率不低于保留。
      (它的效率并不低,因为nsstring足够智能,无法实际执行复制。)

    • 如果您的财产通过 nsmutableString实例 那么答案是” 是的 “-复制比保留效率低。
      (它效率较低,因为必须进行实际的内存分配和复制,但这可能是一件可取的事情。)

    • 一般来说,“复制”财产可能效率较低,但通过使用 NSCopying 协议,可以实现一个“和保留一样高效”地复制的类。 nsstring实例 就是一个例子。

    一般情况下(不只是针对nsstring),什么时候应该使用“copy”而不是“retain”?

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

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

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

    无用 复制 . 如果您的类确实是不可变的,那么实现 窥镜 协议使类在 复制 使用。如果你这样做:

    • 您班的其他用户在使用时将获得性能优势 复制 .
    • 这个 复制 注释使您自己的代码更易于维护 复制 注释表示您确实不需要担心这个对象在其他地方更改状态。
        4
  •  39
  •   Johannes Fahrenkrug    11 年前

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

    • 我想坚持住吗 价值 对象的 在我分配它的时候 我的财产?使用 复制 .

    • 我想坚持住吗 对象 我不在乎它的内在价值 目前是还是将来?使用 坚强的 (保留)

    举个例子:我想坚持住 名称 “Lisa Miller” 复制 )或者我想保持 Lisa Miller 坚强的 )?她的名字后来可能改为“丽莎·史密斯”,但她仍然是同一个人。

        5
  •  13
  •   Monolo    12 年前

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

    NSMutableString *someName = [NSMutableString stringWithString:@"Chris"];
    
    Person *p = [[[Person alloc] init] autorelease];
    p.name = someName;
    
    [someName setString:@"Debajit"];
    

    如果属性是copy类型,那么,

    将为创建新副本 [Person name] 将保存以下内容的字符串 someName 字符串。现在任何操作 绰号 字符串对 [人名] .

    [人名] 绰号 字符串将具有不同的内存地址。

    但如果保留,

    两个 [人名] 将保持与somename字符串相同的内存地址,只是somename字符串的保留计数将增加1。

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

        6
  •  4
  •   Clarkeye    13 年前

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

    p.name = [someName copy];
    

    当然,当设计包含该属性的对象时,只有您知道设计是否受益于分配复制的模式- Cocoawithlove.com 有以下说法:

    “当setter参数可能可变时,应使用copy访问器。 但是如果没有警告,属性的内部状态就不能更改 “—所以判断你是否能承受意外变化的价值,完全是你自己的决定。想象一下这个场景:

    //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对象会自动获取新值;但是,如果确实使用了它,我们就必须手动确保检测到更改并进行同步。在这种情况下,保留语义可能是可取的;在另一种情况下,复制可能更合适。

        7
  •  1
  •   len Nookaraju    11 年前
    @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
    
        8
  •  0
  •   onmyway133    11 年前

    你应该使用 复制 始终声明nsstring属性

    @property (nonatomic, copy) NSString* name;
    

    您应该阅读这些内容,以获取有关它是返回不可变字符串(在传递可变字符串的情况下)还是返回保留字符串(在传递不可变字符串的情况下)的更多信息。

    NSCopying Protocol Reference

    通过保留原始文件而不是创建 类及其内容不可变时的新副本

    Value Objects

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

    - (id)copyWithZone:(NSZone *)zone
    {
        return self;
    }
    
        9
  •  0
  •   jack    10 年前

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

        10
  •  -1
  •   Echilon Mafarnakus    12 年前

    因为名称是(不可变的) NSString 复制或保留如果设置另一个 非字符串 说出姓名。换言之,copy的行为与retain类似,将引用计数增加一个。我认为这是对不可变类的自动优化,因为它们是不可变的,不需要克隆。但是当 NSMutalbeString mstr 设置为“名称”,内容为 MSTR 为了正确起见将被复制。