今天我正在接受采访并被问到一个问题:
使用手动引用计数手动生成setter和getter以进行正确声明:
@interface SomeClass : NSObject
{
NSMutableArray* _array;
}
@property (copy) NSArray* array;
@end
我的回答是:
- (NSArray *)array
{
@syncronized (self)
{
return [_array copy];
}
}
- (void)setArray:(NSArray *)array
{
@synchronized (self)
{
if (_array != array)
{
[_array release];
_array = [array mutableCopy];
[_array retain]
}
}
}
我从未使用过MRC,所以不确定答案的正确性。请帮我用说明更正此代码!
答案 0 :(得分:5)
我是one of the linked topics的作者,我想现在我理解MRC足以在这里写下这个答案:
1)你显然是在getter中泄露了这个副本(在评论中也看到了它) - 所以它应该通过相应的autorelease
调用来平衡。
另请注意,getter中的副本是完成的,因为您需要返回不可变对象,而不是因为使用(copy)声明的@properties的getter你这样做!
2)在retain
之后,你的二传手不应该mutableCopy
,因为mutableCopy已经为你做了+1。
请参阅Advanced Memory Management Programming Guide
中的以下引用基本内存管理规则。
您拥有自己创建的任何对象
使用名称以“alloc”,“new”,“copy”或“mutableCopy”开头的方法(例如,alloc,newObject或mutableCopy)创建对象。
并且
使用保留计数实施所有权政策
所有权政策通过引用计数实现 - 通常在retain方法之后称为“保留计数”。每个对象都有一个保留计数。
创建对象时,其保留计数为1.
3)在我的主题评论中,@robmayoff分享了运行时开源实现的链接:reallySetProperty in objc-accessors.mm,背后有以下推理:
不幸的是,非原子保留和复制设置器具有不必要的竞争条件。如果在线程1上,setter释放_count,并且在线程2上,getter在线程1设置_count = [count retain]之前访问_count,则线程2可以访问解除分配的对象。始终在释放旧值之前将新值存储在_count中。 Objective-C运行时中的真实访问器正确执行它。请参阅objc-accessors.mm中的reallySetProperty。 - rob mayoff
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];
}
在评论中回答你的问题:
这是正常的,真的可以用于日常练习吗?
我想说,它可以在日常实践中使用,并考虑以下因素:
1)您应该将ivar声明移动到私人类别@interface SomeClass ()
,无论是在.m文件中还是私人类扩展中。
2)你应该让你的getter / setter nonatomic ,因为这个属性的原子性在你的肩上(你已经在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
这个设置看起来很有希望虽然我还没有真正测试过像你这样的情况。
P.S。最近我对这个回到过去的手动参考计数做了一些研究,让我与大家分享以下链接,我发现这个链接是最好的: