NSString @property,使用copy而不是retain

16

我正在查看苹果的样例应用程序EditableDetailView,注意到在其中一个控制器中,他们使用(nonatomic,copy)设置NSString属性的实例。什么时候会使用copy而不是retain?这样做是否是为了能够创建独立的副本而不影响现有数据?


2
建议对于可变/不可变组合的类簇使用copy,如 NSString/NSMutableString, NSArray/NSMutableArray, NSDictionary/NSMutableDictionary, NSSet/NSMutableSet。请参考 @Abizern 的答案这里 - Kjuly
3个回答

38

是的,这样可以在不影响现有数据的情况下生成唯一的副本。合成的setter大致看起来像这样:

// For @synthesize(nonatomic, retain) foo:
- (void) setFoo(NSFoo *theFoo)
{
    [theFoo retain];  // retain new value
    [foo release];    // release old value, if any
    foo = theFoo;     // assign new value
}

// For @synthesize(nonatomic, copy) foo:
- (void) setFoo(NSFoo *theFoo)
{
    NSFoo* newFoo = [theFoo copy];  // make copy
    [foo release];  // release old value, if any
    foo = newFoo;   // assign new value
}

注意操作的顺序很重要——在进行自我赋值时,必须先保留/复制新值,然后再释放旧值。如果您先释放旧值,然后将属性分配给自身,则可能会意外释放该值。另请注意,如果旧值为 nil ,则向其发送 release 消息是可以的,因为向 nil 对象发送消息是明确允许且不执行任何操作。

保留和复制的选择只是确定对象属性是否与所设置的值共享相同的值。请考虑以下代码:

// suppose the 'foo' property is declared 'retain' and the 'bar' property is
// declared 'copy'
NSFoo *foo = ...;
NSBar *bar = ...;
someObject.foo = foo;
someObject.bar = bar;
[foo changeInternalState];  // someObject.foo also changes, since it's the same object
[bar changeInternalState];  // someObject.bar does NOT change, since it's a copy

你的最后一行可能有错别字。 其中 //someObject.foo does NOT... 应该是 //someObject.bar does NOT。 - Michael Z
要在自定义的类对象上调用copy方法,您必须重写copyWithZone:方法。 - geekay

3
请记住有一个NSMutableString。 如果您能够更改某个其他对象拥有的字符串的内容(例如通过删除其一半的字符),那将非常糟糕,特别是如果您没有意识到自己正在影响另一个对象。 因此,让其他对象制作自己的副本很好。
您可能会说:“嗯,我为什么不在分配之前复制字符串?”。 也许该对象想要可变字符串,而您的字符串是不可变的。 如果必须先复制字符串,则必须在其文档或标头中查找它想要的字符串类型,然后进行正确类型的复制(每次都要这样做)。 使用这种方式,您只需说other.string = myString,它将制作它想要的任何一种副本-您无需担心。

0

由于某些原因,这篇文章出现在我正在回答的后续问题之上。

你的意思是我必须在释放“foo”之前复制“the foo”对象吗?但如果我在复制“the foo”之前释放“foo”,会有什么问题吗?因为它们是两个不同的对象,我不明白为什么释放一个会影响另一个!

大多数情况下,你是正确的。如果它们实际上是两个独立的对象,那就没关系了。问题在于可能会将同一对象重新分配给自身。如果你这样说:

[myObject setFoo: moof];
[myObject setFoo: moof];

然后第二次你这样做时,在复制之前你会释放moof。在此期间,如果moof的保留计数变为零,则moof将被删除,接下来你将没有任何东西可供复制。Foo现在将是nil。

这种情况可能发生吗?可能比你想象的更多。例如,用户有时可能会点击“更新”按钮两次。

我希望这对您有所帮助并且易于理解。


1
Stack Overflow是一个问答网站,而不是论坛。默认情况下,回答(包括你本来要回复的“问题”)会按投票数排序。你的回答得到了0个赞同,而另一个回答(现在已被删除)得到了-3个赞同,因此你的回答排名更高(对于我们中有足够声望查看其他回答的人来说)。正确的做法应该是在其他回答上发表评论,但这需要50个声望点,而你没有。 (此外,你的回答太深入和格式丰富,无法适合评论。)在你的情况下,没有正确的方法。 - Peter Hosey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接