我是one of the linked topics 的作者,我想现在我对 MRC 的了解足以在这里写下这个答案:
1) 您显然在 getter 中泄漏了副本(在 cmets 中也可以看到)-因此应该通过相应的 autorelease 调用来平衡它。
还请注意,您的 getter 中的这个 copy 已完成,因为您需要返回不可变对象,而不是因为使用 (copy) 声明的 @properties 的 getter 需要你这样做!
2) 你的 setter 不应该在 mutableCopy 之后 retain,因为 mutableCopy 已经为你做了 +1。
请参阅Advanced Memory Management Programming Guide 的以下引用
基本内存管理规则。
您拥有您创建的任何对象
您使用名称以“alloc”、“new”、“copy”或“mutableCopy”开头的方法创建对象(例如,alloc、newObject 或 mutableCopy)。
还有
使用保留计数实施所有权政策
所有权策略是通过引用计数实现的——通常在保留方法之后称为“保留计数”。每个对象都有一个保留计数。
当您创建一个对象时,它的保留计数为 1。
3) 在我的主题 cmets @robmayoff 分享了运行时开源实现的链接:reallySetProperty in objc-accessors.mm,背后的原因如下:
不幸的是,非原子保留和复制设置器具有不必要的竞争条件。如果在线程 1 上,setter 释放 _count,而在线程 2 上,getter 在线程 1 设置 _count = [count retain] 之前访问 _count,则线程 2 可能会访问一个已释放的对象。在释放旧值之前,始终将新值存储在 _count 中。 Objective-C 运行时中的真正访问器正确地完成了它。请参阅 objc-accessors.mm 中的 reallySetProperty。 – 抢劫梅奥夫
4) 您的示例也缺少 dealloc,因为您要在 MRC 下编写它。
5) [IMO,可能是主观的] 由于您的 setter 正在创建数组参数的副本,因此您不需要检查 if (_array != array),因为我相信 (copy) setter 的任务是生成 传递的内容的副本,所以我认为可以省略。
考虑到这些要点,我将编写您的示例,如下所示:
- (NSArray *)array
{
id array;
@synchronized (self)
{
array = [_array copy];
}
return [array autorelease];
}
- (void)setArray:(NSArray *)array
{
id oldValue;
@synchronized (self)
{
oldValue = _array;
_array = [array mutableCopy];
}
[oldValue release];
}
- (void)dealloc {
[_array release];
[super dealloc];
}
在 cmets 中回答您的问题:
正常吗,真的可以在日常练习中使用吗?
我想说,它可以用于日常练习,但需要考虑以下额外注意事项:
1) 您应该将您的 ivar 声明移到私有类别 @interface SomeClass () 中,无论是在您的 .m 文件中还是私有类扩展名中。
2) 你应该让你的 getter/setter 非原子,因为这个属性的原子性在你的肩上(你已经在你自己的 setter 和 getter 中同步了)。
3) 另请参阅链接主题中的the setup,它省略了 ivar 并使用第二个 @property 声明。在您的情况下,它看起来像这样:
// .h
@interface SomeClass : NSObject
@property (nonatomic, strong, readonly) NSArray *array;
@end
// .m or private class extension
@interface SomeClass()
@property (nonatomic, strong) NSMutableArray *array;
@end
@implementation SomeClass
// and here your getters/setters
@end
这个设置看起来很有希望,虽然我还没有真正测试过像你这样的情况。
附:最近我对这个回到过去的手动引用计数做了一些研究,让我与你分享以下我认为在这个主题上最好的链接: