具有自定义 getter 的原子属性

Mag*_*ave 4 cocoa multithreading atomic objective-c lazy-initialization

我有一个属性声明为的类:

@property (nonatomic, strong) NSMutableArray *links;
Run Code Online (Sandbox Code Playgroud)

我想懒惰地实例化它,所以有以下自定义 getter:

- (NSMutableArray *)links {

    if (!_links) {
        _links = [NSMutableArray array];
    }

    return _links;
}
Run Code Online (Sandbox Code Playgroud)

我的应用程序取得了一些进展,现在可以从不同的线程访问该对象。我将声明更改为:

@property (atomic, strong) NSMutableArray *links;
Run Code Online (Sandbox Code Playgroud)

这会生成一个编译器警告:可写原子属性不能将合成的 setter 与用户定义的 getter 结合起来。

我明白 - 我想。 我的问题是,为了使用自定义 getter 创建原子属性,执行以下操作是正确的吗?

  • 创建我自己的实例变量 _links
  • 包裹我的吸气剂 @synchronized
  • 创建一个用户定义的 setter,也包含在 @synchronized

编辑:这是我的新自定义 setter 和 getter 的代码:

- (NSMutableArray *)links {

   if (!_links) {
       @synchronized(self) {
           if (!_links) {
               _links = [NSMutableArray array];
           }
       }
   }

   return _links;    
}

- (void)links:(NSMutableArray *)links {
       @synchronized(self) {
         _links = links;
    }
 }
Run Code Online (Sandbox Code Playgroud)

Ken*_*ses 5

首先,仅仅因为可以从多个线程访问该属性并不意味着 a) 它需要是原子的,也不是 b) 使其原子化就足以使其成为线程安全的。

特别是,属性类型是NSMutableArray*,这意味着任何调用者都可以获取可变数组并且可以不受限制地对其进行变异,这本质上是线程不安全的。您基本上永远不应该创建具有可变类型的属性。对属性的所有更改都必须通过访问器方法,以便您可以控制或至少对更改做出反应。

其次,您的 getter 正在使用双重检查锁反模式。它在像 Objective-C 这样的基于 C 的语言中是不安全的。不要那样做。

也就是说,如果您使属性原子化并实现 getter 或 setter,那么您必须同时实现两者,并且您还负责实现同步以强制执行原子性,这是正确的。使用@synchronized()是合适的,只要您不使用双重检查锁定方法。

但是您确实需要使您的对于该属性(以及可以从多个线程访问的任何其他属性)而言是线程安全的,这需要的不仅仅是使属性原子化。