Извлечение объектов Core Data в фоновом режиме: объекты не повреждены - PullRequest
2 голосов
/ 09 декабря 2011

Мне нужна помощь в использовании объектов из Core Data с GCD;Кажется, я получаю NSManagedObjects, которые не попадают в основной поток, даже когда я получаю доступ к их свойствам.Буду признателен за помощь.

Это то, что я делаю: при запуске мне нужно загрузить список людей из БД базовых данных, выполнить некоторую пользовательскую обработку в фоновом режиме, а затем перезагрузить таблицу, чтобы показатьимена.Я следую рекомендациям по многопоточности Core Data, передавая только objectID в очереди GCD.Но когда я перезагружаю табличное представление в главном потоке, я никогда не вижу имя (или другие свойства), отображаемое для контактов, и при ближайшем рассмотрении NSManagedObjects оказывается ошибками в главном потоке, даже если я обращаюсь к различным свойствам вcellForRowAtIndexPath.Свойство name видно в фоновом потоке, когда я его NSLog;и он также правильно отображается в главном потоке в NSLogs в cellForRowAtIndexPath.Но они не отображаются в tableView, независимо от того, что я делаю.Я попытался получить доступ к свойству name с помощью точечной нотации, а также valueForKey, но ни одна из них не сработала.

Вот мой код….он вызывается из инициализатора FRC:

- (NSFetchedResultsController *)fetchedResultsController 
{
    if (__fetchedResultsController != nil) 
    {
        return __fetchedResultsController;
    }

    __fetchedResultsController = [self newFetchedResultsControllerWithSearch:nil]; // creates a new FRC

    [self filterAllContactsIntoDictionary: __fetchedResultsController];
    return [[__fetchedResultsController retain] autorelease];
}  


- (void) filterAllContactsIntoDictionary: (NSFetchedResultsController *) frc
{

    NSArray *fetchedIDs = [[frc fetchedObjects] valueForKey:@"objectID"];
    NSArray *fetched = [frc fetchedObjects];

    if (filterMainQueue == nil) {
        filterMainQueue = dispatch_queue_create("com.queue.FilterMainQueue", NULL);
    }
    dispatch_async(self.filterMainQueue, ^{

        NSManagedObjectContext *backgroundContext = [[[NSManagedObjectContext alloc] init] autorelease];
        [backgroundContext setPersistentStoreCoordinator:[[self.fetchedResultsController managedObjectContext] persistentStoreCoordinator]];
        NSMutableArray *backgroundObjects = [[NSMutableArray alloc] initWithCapacity: fetchedIDs.count];

        // load the NSManagedObjects in this background context
        for (NSManagedObjectID *personID in fetchedIDs)
        {
            Person *personInContext = (Person *) [backgroundContext objectWithID: personID];
            [backgroundObjects addObject:personInContext];
        }
        [self internal_filterFetchedContacts: backgroundObjects]; // loads contacts into custom buckets

        // done loading contacts into character buckets ... reload tableview on main thread before moving on
        dispatch_async(dispatch_get_main_queue(), ^{

            CGPoint savedOffset = [self.tableView contentOffset];
            [self.tableView reloadData];
            [self.tableView setContentOffset:savedOffset];

        });
    });
}

Что я здесь не так делаю?Есть ли другой способ явно заставить объекты Person вызывать ошибки в главном потоке?Или я что-то не так с очередями GCD и Core Data, о которых я не знаю?Спасибо.

Ответы [ 3 ]

8 голосов
/ 19 декабря 2011

Почему бы не выбрать легкий путь, поскольку вы не сохраняете ничего нового? Вместо создания дополнительного контекста для фонового потока и работы с идентификаторами используйте основной managedObjectContext в фоновом потоке после его блокировки.

например:

- (void) filterAllContactsIntoDictionary: (NSFetchedResultsController *) frc
{

    if (filterMainQueue == nil) {
        filterMainQueue = dispatch_queue_create("com.queue.FilterMainQueue", NULL);
    }
    dispatch_async(self.filterMainQueue, ^{

        NSManagedObjectContext *context = ... // get the main context.
        [context lock];     // lock the context.

        // do something with the context as if it were on the main thread.

        [context unlock];   // unlock the context.

        dispatch_async(dispatch_get_main_queue(), ^{
           CGPoint savedOffset = [self.tableView contentOffset];
           [self.tableView reloadData];
           [self.tableView setContentOffset:savedOffset];
       });
   });
}

Это работает для меня, когда я вызываю метод с performSelectorInBackground, поэтому я думаю, что это должно работать и для диспетчеризации GCD.

0 голосов
/ 16 декабря 2011

РЕДАКТИРОВАТЬ: исходный ответ удален, OP не загружается в фоновом режиме

Я посмотрел ближе на ваш код, и не похоже, что вы делаете что-то, что изменит данные и / или повлияет на контекстосновной поток.

  1. У вас есть fetchedResultsController в основном потоке.Предположительно, это работает, и ваша таблица заполняется данными.Это правда?

  2. Когда вызывается filterAllContentsIntoDictionary, вы передаете массив текущих идентификаторов объектов fetchedResultsController в фоновый поток и выполняете некоторую обработку по ним (предположительно, фильтруя их).на основании некоторых критериев), но вы не меняете данные и сохраняете backgroundContext.

  3. internalFilterFetchedContents - черный ящик.Не зная, что вы собираетесь сделать, трудно сказать, почему он не работает.

  4. Когда это будет сделано, вы перезагрузите таблицу в главном потоке.

Вы не внесли никаких изменений в хранилище, контекст или fetchedResultsController, поэтому, конечно, в таблице показаны те же данные, что и раньше.Отсутствующие детали, которые помогут вам в дальнейшем:

  1. Ваш tableView показывает правильные данные с fetchedResultsController для начала?Если нет, то, скорее всего, ваша единственная проблема заключается в обработке методов tableView делегата и источника данных, а остальное на самом деле не имеет значения.

  2. Что вы собираетесь делать в filterAllContentsIntoDictionary и internalFilterFetchedContents?

  3. Если вы хотите отфильтровать данные, отображаемые fetchedResultsController, не уверены, что вам нужно что-то делать в фоновом режиме.Если вы измените fetchRequest и снова выполните performFetch, ваша таблица будет перезагружена на основе новых результатов.

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

удачи!

0 голосов
/ 09 декабря 2011

Ну, mergeChangesFromContextDidSaveNotification: твой друг. Вам нужно будет сообщить MOC в главном потоке, что в другом месте произошли изменения. Это сделает свое дело.

Вот документация Apple . Цитировать оттуда:

Этот метод обновляет все объекты, которые были обновлены в другом контексте, сбои во всех вновь вставленных объектах и ​​вызывает deleteObject :: для тех, которые были удалены.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...