2016-12-10 4 views
0

У меня есть следующий отчет аварии из моего выпущенном приложения:Ядра параллелизма данных `performBlockAndWait:` NSManagedObjectContext зомби

enter image description here

synchronizeMyWords метод извлекает объекты из базы данных, создает контекст собственной очереди с основным контекстом родителем и, наконец, сохраняет результаты. Все операции находятся в фоновом потоке. Этот метод вызывается каждый раз, когда приложение переходит в background и foreground. Вот упрощенный метод:

- (AWSTask *)synchronizeMyWords { 
    __weak typeof(self) weakSelf = self; 

    AWSContinuationBlock block = ^id _Nullable(AWSTask * _Nonnull task) { 
    if ([task.result isKindOfClass:[NSArray class]]) { 
     NSArray * records = (NSArray *)task.result; 
     NSManagedObjectContext * context = [NSManagedObjectContext MR_contextWithParent:[NSManagedObjectContext MR_defaultContext]]; 
     [context performBlockAndWait:^{ 
     for (NSDictionary * info in records) { 
      [RDRWord MR_createEntityInContext:context]; 
     } 

     [context save:nil]; 
     }]; 
     return [AWSTask taskWithResult:@YES]; 
    } 
    return [AWSTask taskWithError:[NSError errorWithDomain:@"" code:404 userInfo:nil]]; 
    }; 

    AWSExecutor * executor = [AWSExecutor defaultExecutor]; 


    return [[self loadLocalWords] continueWithExecutor:executor withBlock:block]; 
} 

Как вы видите, я использую Magical Record 3rd библиотеку партии управлять стеком Core Data. Вот метод создания контекста собственной очереди:

+ (NSManagedObjectContext *) MR_contextWithParent:(NSManagedObjectContext *)parentContext 
{ 
    NSManagedObjectContext *context = [self MR_newPrivateQueueContext]; 
    [context setParentContext:parentContext]; 
    [context MR_obtainPermanentIDsBeforeSaving]; 
    return context; 
} 

Вы можете проверить всю NSManagedObjectContext+MagicalRecord категории на GitHub here.

Как доступно, что context объект внутри performBlockAndWait: выпущен до того, как он выходит из области? Я лично не могу воспроизвести авария, но многие мои пользователи (устройства IOS 8.1 - 10) затронуты этой проблемой.

UPDATE 1:

Вот, например, тот же доклад на blog

ответ

1

основные данные предоставляют широкие интерфейсы API для решения фоновых потоков. Они также доступны через Magical Record.

Похоже, что вы создаете слишком много потоков без необходимости. Я думаю, что занятость AWSContinuationBlock и AWSExecutor - это не очень хорошая идея. synchronizeMyWords можно было бы вызвать из фоновой нити. Блок может быть запущен в фоновом потоке. Внутри блока создается новый фоновый поток, связанный с дочерним контекстом. Непонятно, что возвращает loadLocalWords, или как continueWithExecutor:block: имеет дело с темами.

Существует также проблема с сохранением данных. Основной контекст не сохраняется после сохранения детского контекста; вероятно, это происходит позже, но, возможно, в связи с какой-то другой операцией, так что тот факт, что ваш код работал раньше, возможно, скорее «ложноположительный».

Моя рекомендация состоит в том, чтобы упростить код резьбы. Вы должны ограничиться API-интерфейсами блока Core Data.

+0

Исправлен основной контекст не сохраняется; основной контекст сохраняется, когда приложение переходит в фоновый режим. Это отдельная операция. 'continueWithExecutor: block:' означает, что 'continuationBlock' работает в фоновом потоке. Задачей здесь является [BFTask] (https://github.com/BoltsFramework/Bolts-ObjC) подкласс, через краткое описание ссылки. –

1

Я отметил, что @Mundi ответил правильно, потому что он написал общий подход, за которым следует следовать. Теперь я хочу поделиться здесь, как я отлаживал его. Во-первых, я узнал, что в xcode можно включить отладочное утверждение параллелизма. Вам необходимо пройти следующий аргумент при запуске:

-com.apple.CoreData.ConcurrencyDebug 1

enter image description here

Теперь в вашем выходе приложения, вы должны увидеть сообщение журнала:

2016-12-12 01:58:31.665 your-app[4267:2180376] CoreData: annotation: Core Data multi-threading assertions enabled.

После того, как я включил его on, мое приложение разбилось на synchronizeMyWords метод (честно говоря, не только там. Интересно, почему Apple не включает в себя параллельную задницу по умолчанию в режиме отладки?). Я проверил, что defaultExecutor в AWSCore library и увидел это:

+ (instancetype)defaultExecutor { 
    static AWSExecutor *defaultExecutor = NULL; 
    static dispatch_once_t onceToken; 
    dispatch_once(&onceToken, ^{ 
     defaultExecutor = [self executorWithBlock:^void(void(^block)()) { 
      // We prefer to run everything possible immediately, so that there is callstack information 
      // when debugging. However, we don't want the stack to get too deep, so if the remaining stack space 
      // is less than 10% of the total space, we dispatch to another GCD queue. 
      size_t totalStackSize = 0; 
      size_t remainingStackSize = remaining_stack_size(&totalStackSize); 

      if (remainingStackSize < (totalStackSize/10)) { 
       dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block); 
      } else { 
       @autoreleasepool { 
        block(); 
       } 
      } 
     }]; 
    }); 
    return defaultExecutor; 
} 

Согласно их if заявление, мой continuationBlock не был гарантированно выполнен на DISPATCH_QUEUE_PRIORITY_DEFAULT очереди. Итак, я создал одну общую очередь dispatch_queue_t и называет все операции над ней, комбинируя метод performBlockAndWait: CoreData. В результате сейчас никаких сбоев нет, и я представил новый выпуск. Я обновлю это сообщение, если у меня нет отчета о сбоях с context зомби.

+0

Спасибо за обмен! Он считает, что это полезно для других. – Mundi

Смежные вопросы