1

Я в настоящее время осуществляю многопоточные приложения и я сталкиваюсь странное состояние гонки (что приводит к проблеме АРКА: ошибки для объекта 0x7f8bcbd6a1c0: указатель освобождения не была выделен).IOS параллелизм: NSOperationQueue и ARC вопрос

Приложение создает несколько NSOperations, каждый из которых загружает и обрабатывает информацию. Как и в каждом из этих NSOperations, может произойти ошибка (например, веб-служба недоступна), я хочу распространять ошибку, чтобы я мог ее обработать. Однако, похоже, я сталкиваюсь с состоянием гонки, и операции пытаются получить доступ к недопустимой памяти.

Минималистичный пример, который показывает проблему доступа к памяти заключается в следующем:

- (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 (code = EXC_I386_GPFLT) в отладчике.

Я добавил for-Loop более 10000 итераций только для увеличения вероятности возникновения гонки. Если я оставлю это, ошибка возникает только в редких случаях (но все же должна быть исправлена, очевидно, что я делаю что-то неправильно здесь).

+1

Я думаю, что нашел. Я синхронизируюсь на ноль, который фактически не выполняет никакой синхронизации. – Trapper

ответ

0

Директива @synchronized использует объект, который вы передаете для управления синхронизацией, поэтому, как вы заметили, вы пытаетесь синхронизировать с nil. И даже если это не было nil, объект, на который ссылается директива @synchronized, каждый раз представляет собой другой объект, в значительной степени побеждающий попытку синхронизации.

Самое простое изменение заключается в использовании @synchronized(self). Или создайте объект NSLock, а затем lock и unlock его при доступе globalError. Или, если это была ситуация с высоким спросом, используйте очередь GCD (либо последовательную очередь, либо шаблон чтения-записи).