线程安全枚举NSArray

时间:2014-05-26 13:49:42

标签: objective-c thread-safety nsarray grand-central-dispatch

以下是线程安全的吗?

@property (nonatomic, copy) NSArray *objects;
…
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^
{
    for (MyClass *current in weakSelf.objects) {
       current.property = 0.0;
    }
});

特别是我不确定将self.object设置为不同的NSArray时会发生什么,而我在后台线程中枚举它。

是否继续枚举旧的NSArray?

只要我枚举一个NSArray(而不是NSMutableArray),我就不需要@syncronize了吗?

2 个答案:

答案 0 :(得分:2)

快速枚举检测正在枚举的集合是否发生变化,例如, this nice blog。此外,如果一个块是异步执行的,它会保持对self的引用,来自here的引用:" ...从编译器的角度来看,传递给的块dispatch_async会引用self ..."。
我认为结果是,当您更改指向集合的指针时,异步执行的快速枚举将引发异常。

答案 1 :(得分:2)

这不是线程安全的。

首先,该物业是非原子的。因此,当该线程仍然在getter方法内部时,在其指针被放入适当的寄存器并返回时间控制的点之间时,无法保证数组尚未被另一个线程释放(因此可能被释放)回到调用代码。也就是说,从这个线程的角度来看,该方法可以返回一个无效的悬空指针。

即使指针在您输入for循环时仍然有效,快速枚举可能需要多次向数组发送消息。虽然-objects getter只会被调用一次,但我认为你不能确定ARC会为你保留返回的数组(假设你正在使用ARC)。如果另一个线程将该属性设置为引用另一个数组,则setter将释放旧数组。如果那是最后一个引用,则可以释放该数组(并释放所有包含的对象)。正在进行的枚举可能会引用解除分配的对象。

在块的内部和设置器(或调用setter的任何东西)周围使用@synchronized(self)就足以使其安全。但是,由于您已经将块调度到队列,因此应考虑使用自定义队列而不是@synchronized()来协调对该属性的操作。队列应该是串行的,或者它可以是并发的,并且您必须对所有写操作使用屏障任务。

或者,我认为使属性成为原子就足够了。文档说,原子属性的getter将执行某些喜欢获取锁定,将实例变量复制到临时变量,保留并自动释放它,释放锁定,并返回临时值变量