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

时间:2014-02-15 21:58:33

标签: objective-c

今天我正在接受采访并被问到一个问题:

使用手动引用计数手动生成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,所以不确定答案的正确性。请帮我用说明更正此代码!

1 个答案:

答案 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。最近我对这个回到过去的手动参考计数做了一些研究,让我与大家分享以下链接,我发现这个链接是最好的: