使用MRC的可变ivar的不可变特性

Ale*_*lex 1 objective-c

今天我接受采访时被问到一个问题:

手动生成二传手和吸气剂,使用手动参考计数进行适当的声明:

@interface SomeClass : NSObject 
{
     NSMutableArray* _array; 
}
@property (copy) NSArray* array;

@end
Run Code Online (Sandbox Code Playgroud)

我的回答是:

- (NSArray *)array 
{
     @syncronized (self)
     {
         return [_array copy];
     }  
}

- (void)setArray:(NSArray *)array 
{    
    @synchronized (self)    
    {    
      if (_array != array)
      {
         [_array release];
         _array = [array mutableCopy];
         [_array retain]
      }    
    } 
}
Run Code Online (Sandbox Code Playgroud)

我从未使用过MRC,所以不确定答案的正确性.请帮我修改这段代码并附上说明!

Sta*_*ich 5

我是其中一个链接主题的作者,我想现在我理解MRC足以在这里写下这个答案:

1)你显然是在getter中泄露了这个副本(在评论中也看到了它) - 所以它应该通过相应的autorelease调用来平衡.

还要注意,你的getter 中的这个副本是完成的,因为你需要返回不可变对象,而不是因为声明了@properties ( get)的getter 要求你这样做!

2)你的setter不应该retain在之后mutableCopy,因为mutableCopy已经为你做了+1.

请参阅" 高级内存管理编程指南"中的以下引用

基本内存管理规则.

您拥有自己创建的任何对象

使用名称以"alloc","new","copy"或"mutableCopy"开头的方法(例如,alloc,newObject或mutableCopy)创建对象.

使用保留计数实施所有权政策

所有权策略通过引用计数实现 - 通常在retain方法之后称为"保留计数".每个对象都有一个保留计数.

创建对象时,其保留计数为1.

3)在我的主题评论中@robmayoff分享了运行时的开源实现的链接:objc-accessors.mm中的reallySetProperty,后面有以下推理:

不幸的是,非原子保留和复制装置具有不必要的竞争条件.如果在线程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];
}
Run Code Online (Sandbox Code Playgroud)

在评论中回答你的问题:

它是正常的,真的可以用于日常实践吗?

我想说,它可以用于日常实践中,并考虑以下因素:

1)您应该将ivar声明移动到@interface SomeClass ().m文件或​​私有类扩展中的私有类别中.

2)你应该让你的getter/setter 原子,因为这个属性的原子性在你的肩上(你已经在setter和getter中自己同步).

3)另请参阅链接主题的设置,该设置省略了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
Run Code Online (Sandbox Code Playgroud)

这个设置看起来很有希望虽然我还没有真正测试过像你这样的情况.


PS最近我对这个回到过去的手动参考计数做了一些研究,让我与大家分享以下链接,我发现这个主题是最好的: