Вопрос по ios – NSFetchedResultsController не показывает обновления из другого контекста

22

у меня естьNSFetchedResultsController и несколько операций обновляют управляемые объекты в отдельных потоках черезNSOperationQueue.

FRC (с его предикатом) выглядит так:

- (NSFetchedResultsController*)fetchedResultsController
{
    if(fetchedResultsController) return fetchedResultsController;

    NSManagedObjectContext* mainContext = [self managedObjectContext];

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    [fetchRequest setEntity:[NSEntityDescription entityForName:@"Check" inManagedObjectContext:mainContext]];
    [fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"isSync == %@", [NSNumber numberWithBool:NO]]];
    [fetchRequest setFetchBatchSize:10];

    fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:mainContext sectionNameKeyPath:nil cacheName:nil];
    fetchedResultsController.delegate = self;

    [fetchRequest release], fetchRequest = nil;

    return fetchedResultsController;
}

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

В рамках резьбовой операции я меняюisSync собственность отNO вYES, Чтобы знать, что этоCheck сущность для обновления, основной контекст переходит к многопоточномуNSManagedObjectID. The threaded operation retrieves the managed object like the following:

-(void)main
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    NSManagedObjectContext *exportContext = [[NSManagedObjectContext alloc] init];
    [exportContext setPersistentStoreCoordinator:[self persistentStoreCoordinator]];

    //...

    Check* check = (Check*)[exportContext existingObjectWithID:objID error:&error];
    check.isSync = [NSNumber numberWithBool:YES];

    //...

    [exportContext save:&error];

    [pool release], pool = nil;
}

Когда операция потока вызываетsave mergeChangesFromContextDidSaveNotification вызывается уведомление, и основной контекст объединяет изменения.

- (void)contextChanged:(NSNotification*)notification
{
    if ([notification object] == [self managedObjectContext]) return;

    if (![NSThread isMainThread]) {
        [self performSelectorOnMainThread:@selector(contextChanged:) withObject:notification waitUntilDone:YES];
        return;
    }

    [[self managedObjectContext] mergeChangesFromContextDidSaveNotification:notification];
}

Регистрация описаниеnotification приводит к проверке того, что изменения выполнены правильно.

My problem

Делегаты методыNSFetchedResultsControllerDelegate не называются.

Это довольно странно, поскольку работа с одним и тем же контекстом, основным, позволяет прослушивать изменения и вызывать методы делегатов, например, удаление объекта строки вUITableView.

Я нашел несколько тем для SO с такой же проблемой. Я испробовал все обходные пути, но не могу найти ценное решение:

NSFetchedResultsController not showing updates from other contexts

NSFetchedResultsController not firing delegate method after merging update from background thread

NSFetchedResultsController with predicate ignores changes merged from different NSManagedObjectContext

Заранее спасибо.

Edit

The code above was working in a previous model. Then I created a new model copying (and pasting) entities from the previous one and now it doesn't work anymore.

Предложения?

Edit 2

Это предикат, который я использую вNSFetchedResultsController добытчик. Это моя вина, но когда я написал сообщение, я не скопировал его.

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"insertionDate" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];

// previous code here
[fetchRequest setSortDescriptors:sortDescriptors];

Теперь оJody последний комментарий

In the main() of your NSOperation, you are loading new objects, and in there it looks like you are setting isSync to YES for each new object. The predicate you use for the fetchedResultsController is looking only for objects that have isSync == NO.

Я ожидал, что когда собственностьisSync установлено в ДА,NSFetchedResultsController отмечает, что изменяет и удаляет строки, которые не соответствуют предикату. Я ошибся?

Помните, что при объединении изменений из фона в основной поток я вижу, что несколько объектов обновили своиisSync имущество.

Смотрите мой ответ на этот вопрос. Я думаю, что это может помочь:stackoverflow.com/questions/3923826/… Artur Friesen
Прочитав ваш вопрос, я думаю, что @ArturFriesen прав: NSFetchedResultsController игнорирует изменения, которые происходят вне его или дочернего контекста. Вам нужно будет вызывать refreshObject для каждого соответствующего объекта в основном цикле. Еще один способ сделать это:stackoverflow.com/a/21378533/313633 Sjors Provoost

Ваш Ответ

2   ответа
11

поэтому, возможно, где-то в вашем коде ошибка ...

Дважды проверьте, правильно ли вы регистрируетесь, чтобы получать уведомления от фонового MOC.

Зарегистрируйтесь, чтобы получать все уведомления от всех объектов. В этом методе зарегистрируйте событие и все его данные. Когда объект является MOC, выведите все его свойства (особенно списки зарегистрированных, вставленных, обновленных и удаленных объектов).

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

Кроме того, вы пропустили много кода, поэтому трудно понять, что вы на самом деле делаете, но пример кода, который вы включили, выглядит так, как будто сложно установить isSync в YES для всех загружаемых объектов, но ваш запрос на выборку требует только тех, кто имеет isSync установлен на NO. Ни один из этих новых объектов не пройдет этот предикат.

Наконец, дважды проверьте определение вашей модели и убедитесь, что вы используете правильный тип номера. Это может быть большим источником проблем.

EDIT

Ах да, я забыл ... у вашего запроса на выборку нет дескриптора сортировки. Когда вы создаете FRC, ваш запрос на выборку должен содержать хотя бы один дескриптор сортировки ... если у вас есть несколько секций, первый дескриптор сортировки используется для группировки объектов по секциям.

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

Кроме того, я бы предложил использовать родительские / дочерние MOC для этого типа обработки, но то, что вы делаете, должно работать, если все сделано правильно.

Родитель (частный тип параллелизма) Основной (основной тип параллелизма)

Затем, с вашими фоновыми MOC, просто попросите их установить основной moc как своего родителя. Когда они сохраняются, их объекты вводятся непосредственно в основной MOC. Затем основной MOC может выпустить сохранения в более позднее время, чтобы поместить их на диск.

Или вы можете использовать родительский MOC для родительского элемента & quot; parent & quot; а затем «основной» MOC может просто повторить выборку, чтобы получить данные от родителя.

@AlexsanderAkers Спасибо за ваш комментарий. Lorenzo B
Многие фреймворки используют Core Data внутренне. Убедитесь, что вы не объединяете эти уведомления по ошибке.
Использование CoreData может быть настолько сложным, что когда у вас есть проблема, вам почти нужно предоставить весь код, потому что многие вещи тесно связаны. Я бы установил точку останова (или NSLog) прямо перед сохранением, и выгрузил бы весь сохраняемый moc (у меня фактически есть вспомогательный код, чтобы сделать это, это так обычно для меня). Мой канонический процесс отладки для CoreData: у меня есть подклассы для NSManagedObjectContext и NSPersistentStoreCoordinator, которые просто NSLog и вызывают super. Я также регистрируюсь для каждого уведомления, и NSLog их. Я предлагаю вам сделать нечто подобное, чтобы понять, что на самом деле происходит.
@JodyHagins Прежде всего, спасибо за ваш ответ. +1 за вашу поддержку. То, что я написал в редактировании, я думаю, это не проблема. Может быть, я что-то изменил, но я не знаю что. Не могли бы вы объяснить, что вы имеете в виду с...but the code sample you included looks like it is hard setting isSync to YES for all objects being loaded, but your fetch request only wants those with isSync set to NO. None of those new objects will pass that predicate.? Заранее спасибо. Lorenzo B
В main () вашего NSOperation вы загружаете новые объекты, и там, похоже, вы устанавливаете isSync в YES для каждого нового объекта. Предикат, который вы используете для fetchedResultsController, ищет только те объекты, которые имеют isSync == NO. Также, пожалуйста, рассмотрите другие вопросы, которые я поднял (особенно отсутствующий дескриптор сортировки).
2

которую я решил с контекстами родителя / ребенка. Вот проблема, которая у меня была.

Я обновлял свой граф объектов Core Data в фоновом потоке, который имел свой собственныйmanagedObjectContext (что является обязательным), и мойfetchedResultsController не смог получить изменения, внесенные в базу данных.

After I solved it I took some notes:

ManagedObjectContexts не являются потокобезопасными, что означает, чтоmanagedObjectContext не может быть разделен с другими потоками. Если поток должен использоватьmanagedObjectContextзатем он инициализирует свой собственный managedObjectContext.

Для инициализацииmanagedObjectContext Есть два способа:

alloc/init then set its persistentStoreCoordinator property

alloc/init then set a parentContext property instead of a persistentStoreCoordinator property

примечание: нельзя установить обаpersistentStoreCoordinator иparentContext свойствоmanagedObjectContext.

Использование родительского / дочернего контекстов необходимо, когда контекст запускается в фоновом потоке, который"linked to controllers and UI objects that are required to be used only on the main thread"(основные данные документации).

Вот требования, необходимые для родительского / дочернего контекста:

the parent context lives in the main thread

the child context lives in the background thread

both contexts need to be initialized with an initWithConcurrencyType:NSMainQueueConcurrencyType method.

when the batch of changes has been done in the child context, both contexts need to perform a save operation. These save operations need to be nested in performBlock methods, i.e:

childContext performBlock:^{
    [childContext save:nil];
    [self.parentContext performBlock:^{
        [self.parentContext save:nil];                
    }];
}];

РЕДАКТИРОВАТЬ: код выше на самом деле плохая идея по 2 причинам:

1) Работает без сохранения родительского контекста.

2) Основной поток блокируется, если на нем работает родительский контекст.

Надеюсь, это помогло!

РЕДАКТИРОВАТЬ: Вот поток стека overover, который мне очень помог:Нужно ли родительскому элементу Core Data ManagedObjectContext совместно использовать тип параллелизма с дочерним контекстом?

спасибо, что поделились +1, я не понимаюboth contexts need to be initialized with an initWithConcurrencyType:NSMainQueueConcurrencyType method., Что это значит? Lorenzo B
both contexts need to be initialized with an initWithConcurrencyType:,NSMainQueueConcurrencyType method & # X2014; это не верно.

Похожие вопросы