【问题标题】:Immutable property for mutable ivar using MRC使用 MRC 的可变 ivar 的不可变属性
【发布时间】:2014-02-15 21:58:33
【问题描述】:

今天面试被问到一个问题:

手动生成 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,因此不确定答案的正确性。请帮我用描述更正这段代码!

【问题讨论】:

  • 谢谢 Josh,但我问的是可变/不可变 ivar/property。所以我的问题是在这种特殊情况下我需要设置和获取什么
  • 另外我也不知道有没有意义或者只是抽象测试的情况
  • 很久没用MRC了,不过我觉得你在getter中泄露了副本(应该是return [[_array copy] autorelease]
  • @Alex 重写 getter 和 setter 是很正常的,所以你可能会在真实的应用程序中看到这样的代码。但是没有充分的理由使用 MRC,所以在他们雇用你之后将项目转移到 ARC :)

标签: objective-c


【解决方案1】:

我是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

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


附:最近我对这个回到过去的手动引用计数做了一些研究,让我与你分享以下我认为在这个主题上最好的链接:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多