以下是线程安全的吗?
@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了吗?
答案 0 :(得分:2)
快速枚举检测正在枚举的集合是否发生变化,例如, this nice blog。此外,如果一个块是异步执行的,它会保持对self
的引用,来自here的引用:" ...从编译器的角度来看,传递给的块dispatch_async
会引用self
..."。
我认为结果是,当您更改指向集合的指针时,异步执行的快速枚举将引发异常。
答案 1 :(得分:2)
这不是线程安全的。
首先,该物业是非原子的。因此,当该线程仍然在getter方法内部时,在其指针被放入适当的寄存器并返回时间控制的点之间时,无法保证数组尚未被另一个线程释放(因此可能被释放)回到调用代码。也就是说,从这个线程的角度来看,该方法可以返回一个无效的悬空指针。
即使指针在您输入for
循环时仍然有效,快速枚举可能需要多次向数组发送消息。虽然-objects
getter只会被调用一次,但我认为你不能确定ARC会为你保留返回的数组(假设你正在使用ARC)。如果另一个线程将该属性设置为引用另一个数组,则setter将释放旧数组。如果那是最后一个引用,则可以释放该数组(并释放所有包含的对象)。正在进行的枚举可能会引用解除分配的对象。
在块的内部和设置器(或调用setter的任何东西)周围使用@synchronized(self)
就足以使其安全。但是,由于您已经将块调度到队列,因此应考虑使用自定义队列而不是@synchronized()
来协调对该属性的操作。队列应该是串行的,或者它可以是并发的,并且您必须对所有写操作使用屏障任务。
或者,我认为使属性成为原子就足够了。文档说,原子属性的getter将执行某些喜欢获取锁定,将实例变量复制到临时变量,保留并自动释放它,释放锁定,并返回临时值变量