iOS 并发:NSOperationQueue 和 ARC 问题

iOS concurrency: NSOperationQueue and ARC issue

我目前正在实现一个多线程应用程序,我遇到了一个奇怪的竞争条件(导致 ARC 问题:对象 0x7f8bcbd6a1c0 错误:未分配正在释放的指针)。

应用创建了多个NSOperations,每个NSOperations都在下载和处理信息。由于在这些 NSOperations 中的每一个中都可能发生错误(例如,Web 服务不可用),我想向上传播错误以便我可以处理它。但是,我似乎遇到了竞争条件,操作尝试访问无效内存。

下面是一个显示内存访问问题的简单示例:

- (void) createError: (NSError**) err {
    *err = [[NSError alloc] initWithDomain:@"Test" code:12345 userInfo:nil];
}

- (void)viewDidLoad {
    [super viewDidLoad];

    __block NSError *globalError = nil;

    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

    NSOperation *op = nil;
    for (int i=0; i<10; i++) {
        op = [NSBlockOperation blockOperationWithBlock:^{
            NSError *err = nil;
            [self createError:&err];

            // This loop increases the chance to get the race condition
            for (int j=0; j<10000;j++) {
                @synchronized(globalError) {
                      globalError = err;
                }
            }
        }];
        [queue addOperation:op];
    }

    [queue waitUntilAllOperationsAreFinished];

    if (globalError) {
        NSLog(@"An error occured in at least one of the operations");
    }
}

每当我 运行 这个程序时,我都会得到一个异常。大多数情况下,它是对象 0x7fc0b860e860 的 错误:正在释放的指针未分配 ,但是,有时我在调试器中也会遇到 EXC_BAD_ACCESS(代码=EXC_I386_GPFLT)中断.

我添加了超过 10000 次迭代的 for 循环只是为了增加出现竞争条件的机会。如果我离开它,错误只会在极少数情况下发生(但仍然必须修复,显然我在这里做错了)。

@synchronized 指令使用您传递的对象来控制同步,因此,正如您所注意到的,您正在尝试在 nil 上进行同步。即使它不是 nil@synchronized 指令引用的对象每次都是不同的对象,这在很大程度上挫败了同步尝试。

最简单的更改是改用 @synchronized(self)。或者,创建一个 NSLock 对象,然后在访问 globalErrorlockunlock 它。或者,如果这是一个高需求情况,请使用 GCD 队列(串行队列或 reader-writer 模式)。