Автоматический UITableView с использованием NSFetchedResultsController, находясь в фоновом режиме

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

Если так получилось, что есть статьи, которые соответствуют требованиям, и открыт UITableView, который показывает этот конкретный список, то приложение вылетает, как запрос NSFetchedResultsController часто приводит к следующей ошибке:

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'no section at index 26'

или иногда:

*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 0 beyond bounds for empty array'

при вызове: (_objects является NSFetchedResultsController)

[_objects objectAtIndexPath:[NSIndexPath indexPathForRow:currentRow inSection:indexPath.section]]

вызваны

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject

Так что мне было интересно, если это потому, что NSFetchedResultsController (в главном потоке) недоступен (должным образом) для фоновой задачи / потока, когда приложение прекратило работу. Это объяснение? И если так, как я могу предотвратить это правильно?

Я могу предотвратить ManagedObjectContect чтобы объединить изменения, но как мне убедиться, что они обновлены, и изменения отражены в UITableViewкогда приложение вернется на передний план?

Только теория. Я знаю, что сбой происходит в фоновом режиме, но пока не намного. Обновление, удаление, удаление работает нормально, когда приложение активно.

ОБНОВЛЕНО исправлением кода: вызов фонового процесса

- (void)performMaintenance{      
        UIApplication *application = [UIApplication sharedApplication]; //Get the shared application instance

        __block UIBackgroundTaskIdentifier background_task; //Create a task object

        background_task = [application beginBackgroundTaskWithExpirationHandler: ^ {
            [application endBackgroundTask: background_task]; //Tell the system that we are done with the tasks
            background_task = UIBackgroundTaskInvalid; //Set the task to be invalid

            //System will be shutting down the app at any point in time now
        }];

        //Background tasks require you to use asyncrous tasks

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            //Perform your tasks that your application requires

            NSLog(@"\n\nRunning maintenance in the background!\n\n");
            // Perform work that should be allowed to continue in background
            [self doMaintenance];

            [application endBackgroundTask: background_task]; //End the task so the system knows that you are done with what you need to perform
            background_task = UIBackgroundTaskInvalid; //Invalidate the background_task
        });
}


#pragma mark Actions
- (void)doMaintenance{
    DODataManager *data = [[DODataManager alloc] init];

    NSDateComponents *components = [[NSDateComponents alloc] init];
    components.month = -2;
    NSDate *twoMonthsBeforeNow = [[NSCalendar currentCalendar] dateByAddingComponents:components toDate:[NSDate date] options:0];

    NSFetchedResultsController* controller = [data getArticlesBeforeDate:twoMonthsBeforeNow];
    NSLog(@"Cleaning up database, removing %i items", controller.fetchedObjects.count);
    for(DOArticle* article in controller.fetchedObjects){
        [controller.managedObjectContext deleteObject:article];
    }

    // Update the unread count and unread count (if dataconenction available)
    [data updateBadge];

    [data commitChanges];

    data=nil;

    NSMutableDictionary* dict = [NSMutableDictionary dictionaryWithObject:@"1.0" forKey:@"progress"];
    [[NSNotificationCenter defaultCenter] postNotificationName:kDialogUpdate object:dict];

    NSLog(@"Finished maintenace!");
}

Очистка теперь происходит должным образом в отдельном потоке, и уведомления отправляются в основной поток, но проблема остается той же.

2014-01-27 22:16:34.541 iDomsPortalDev[1963:5107] Cleaning up database, removing 2 items
2014-01-27 22:16:36.024 iDomsPortalDev[1963:5107] Start for 3 items on object Setting!
2014-01-27 22:16:36.026 iDomsPortalDev[1963:5107] Removing 0 stat items
2014-01-27 22:16:36.029 iDomsPortalDev[1963:70b] Merging changes!
2014-01-27 22:16:36.030 iDomsPortalDev[1963:70b] Delete

1 ответ

Решение

Вы вызываете doMaintainance из асинхронного блока GCD и получаете доступ к контексту управляемого объекта.

Контексты управляемых объектов не являются потокобезопасными

Другие вопросы по тегам