如何从辅助线程安全地访问NSArray属性的内容?

Rob*_*ger 3 cocoa multithreading objective-c accessor thread-safety

我有一个应用程序(使用retain/release,而不是GC)来维护一个NSArray实例变量,它作为属性公开,如下所示:

@interface MyObject : NSObject
{
    NSArray* myArray;
}
@property (copy) NSArray* myArray;
@end
Run Code Online (Sandbox Code Playgroud)

我想从辅助线程访问此数组的内容,该辅助线程使用分离-performSelectorInBackground:withObject:.在执行辅助线程期间,阵列可能并且实际上可能会发生变化.

在辅助线程中,我想做这样的事情:

if([self.myArray containsObject:foo])
{
    //do stuff
}
Run Code Online (Sandbox Code Playgroud)

从阅读线程文档,似乎我应该能够@synchronized在访问器中使用该指令,如下所示:

@implementation MyObject
- (NSArray *)myArray
{
    NSArray *result;
    @synchronized(self)
    {
        result = [myArray retain];
    }
    return [result autorelease];
}

- (void)setMyArray:(NSArray *)aMyArray
{
    @synchronized(self)
    {
        [myArray release];
        myArray = [aMyArray copy];
    }
}
@end
Run Code Online (Sandbox Code Playgroud)

这是我需要做的就是确保线程安全,还是更复杂?

更新:我随后在Apple网站上发现了一篇很好的文章,深入解决了这个问题:http://developer.apple.com/mac/library/technotes/tn2002/tn2059.html

bob*_*vil 5

上面的代码可以保护您不会同时设置数组,或者在另一个设置数组时获取数组.由于它是一个非可变数组,因此可以很好地保护数组本身.

但是,如果"数组将改变"意味着你将编辑数组中的项目,你仍然可能会遇到一些问题.例如,如果数组中填充了NSMutableStrings,并且您有一个运行的线程:

NSMutableString *foo = [myObject.myArray objectAtIndex:0];
[foo appendString:@"foo"];
Run Code Online (Sandbox Code Playgroud)

和另一个跑了

NSMutableString *bar = [myObject.myArray objectAtIndex:0];
[bar appendString:@"bar"];
Run Code Online (Sandbox Code Playgroud)

对数组的访问是安全的(一个线程必须等待另一个线程才能访问它),但是,访问foo/bar指针(它是相同的)不会,因为对'appendString'的两次调用都是在@synchronized块之外.

如果这是您的阵列将如何更改,您还需要同步这些访问点.有更多@synchronized块或其他类型的锁.请参阅使用锁

  • 但基本问题仍然存在 - 为什么要使用琐碎的自定义访问器?@synthesize myArray; 会做同样的事情,只会更有效率. (4认同)
  • 阿鲁曼击中头部的钉子.声明该属性以支持原子访问.而bobDevil也是对的:这还不足以保证线程安全.如果你发现自己在想,"这足以保证线程安全吗?" 那么答案总是没有.只有当你已经证明你的代码是线程安全的时候就足够了,直到你编写更多的代码.线程安全是一个非常非常脆弱的属性.(并且不要忘记无死锁,无饥饿,活跃,优先倒置......如果你不是绝对需要线程,那么就不要去那里.) (2认同)