Основные данные: NSManagedObjectContext, NSFetchResultsController и UIManagedDocument - PullRequest
0 голосов
/ 24 марта 2012

У меня проблемы с использованием фоновых потоков для обновления данных в UIManagedDocument / Core Data. В частности, я использую NSFetchResultsController для обновления аннотаций карты на основе геокодированных данных из фонового потока (после слияния обратно в мой основной MOC), но карта никогда не обновляется из-за способа, которым UIManagedDocument передает данные в свои хранилища и / или нескольким MOC (родительским и дочерним). Если я закрываю приложение и снова открываю, аннотации заполняются, поэтому в определенный момент происходит фиксация в постоянном хранилище, но мне неясно, как форсировать такой коммит, который, таким образом, обновит NSFetchResultsController. Вот некоторый код:

Фоновый поток, обновляющий MOC:

- (void) populateGPSCoordsInClubsInContext: (NSManagedObjectContext *) mainCtx
{            
    dispatch_queue_t MapFetchQ = dispatch_queue_create("Google Map Data Fetcher", NULL);
    dispatch_async(MapFetchQ, ^{

        NSManagedObjectContext * ctxThread = [[NSManagedObjectContext alloc] init];
        [ctxThread setPersistentStoreCoordinator:mainCtx.persistentStoreCoordinator];


        NSFetchRequest * request = [NSFetchRequest fetchRequestWithEntityName:@"Club"];
        request.predicate = [NSPredicate predicateWithFormat:@"inRegion.name=%@", self.name];
        NSError *error = nil;

        NSArray * clubs = [ctxThread executeFetchRequest:request error:&error];

        NSLog(@"[%@] Fetching map data. Club count is %d", self.name, [clubs count]);  

        int delayCounter = 0;

        for(Club * club in clubs)
        {
            if(![club.hasCoord boolValue] && club != nil)
            {
                delayCounter++; // to deal with google maps api's DoS protection            

                [club setLongitudeAndLattitudeFromGoogle];
                NSError * error;

                if(![ctx save:&error])
                 NSLog(@"[%@] Problem saving region to database.", self.name);

            }

            if(delayCounter == 8)
            {            
                [NSThread sleepForTimeInterval:(NSTimeInterval)2.0];
                delayCounter = 0;
            }
        }
    });
    dispatch_release(MapFetchQ);
}

Когда вызываются эти сохранения, я получаю уведомление в главном потоке (в моем делегате приложения) примерно так:

- (void) contextDidSave: (NSNotification *) notification
{
    NSManagedObjectContext * ctx = [self.clubsDB managedObjectContext];
    [ctx mergeChangesFromContextDidSaveNotification:notification];

    NSArray * updates = [[notification.object updatedObjects] allObjects];

    for(Club * club in updates) // This never fires because updates never has objects
    {
        NSLog(@"*********** %@", club.name);
    }

    NSLog(@"[%@] %@", [self class], NSStringFromSelector(_cmd));

}

И я установил свой контроллер извлеченных результатов следующим образом (предикат верный, результаты ожидаемые с перезагрузкой приложения после передачи данных в хранилище):

-(void) setupFRC
{

    NSFetchRequest * request = [NSFetchRequest fetchRequestWithEntityName:@"Club"];
    request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES]];
    request.predicate = [NSPredicate predicateWithFormat:@"inRegion.name=%@ AND hasCoord=%@",[self.clubsDB regionTitleAsString], [NSNumber numberWithBool:YES]]; // Follow the relationshop and only display clubs from THIS region.

    //request.predicate = [NSPredicate predicateWithFormat:@"inRegion.name=%@",[self.clubsDB regionTitleAsString]];

    self.debug = YES;

    self.fetchedResultsController = 
    [[NSFetchedResultsController alloc] initWithFetchRequest:request 
                                        managedObjectContext:self.clubsDB.managedObjectContext
                                          sectionNameKeyPath:nil
                                                   cacheName:nil];
}

Есть какие-нибудь идеи относительно того, как я могу обновить соответствующий MOC, чтобы заставить выбранный контроллер результатов вести себя как нужно?

Ответы [ 3 ]

0 голосов
/ 03 апреля 2012

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

0 голосов
/ 18 апреля 2012

ОК, я собираюсь отредактировать ваш код, чтобы дать вам представление о том, как он должен выглядеть.Я предполагаю, что вы передаете MOC из UIManagedDocument в populateGPSCoordsInClubsContext.Заметьте, что в том, что вы уже делаете, очень мало различий, но, как мы знаем, одна строка кода может изменить все ...является то, что вам не нужно даже обрабатывать уведомления (по крайней мере, не для согласованности).

Если вы хотите сделать это другим способом, вы можете сделать это ...

        ctxThread.parentContext = document.parentContext;    

Тогда вы не будете вызывать updateChangeCount для документа.Эти изменения войдут в родительский контекст и в файл.Однако при этом вам не нужно обрабатывать уведомления, потому что будущие выборки будут автоматически их видеть.Конечно, если вы хотите обновить информацию об изменениях, вы все равно можете обрабатывать уведомления, но это все.Чтобы увидеть их при получении, вам не нужно больше ничего делать.

Вопреки распространенному мнению, UIManagedDocument действительно достаточно эффективен и прост (если вы знаете правила).

0 голосов
/ 02 апреля 2012

У меня проблема с тем, как UIManagedDocument также отправляет свои коммиты.Единственное решение, о котором я могу подумать, - это прекратить использование UIManagedDocument и просто использовать контекст из PersistentStore, как предусмотрено в шаблоне Master-Detail по умолчанию.

edit: После дальнейших исследований кажется, что нетспособ для UIManagedDocument для фиксации изменений, поэтому, вероятно, лучше передать контекст, созданный из постоянного хранилища.Похоже, что не случайно Apple еще не предоставила полезного примера кода для UIManagedDocument.Я бы придерживался шаблона по умолчанию.

Вот ссылка на кого-то, сталкивающегося с подобной проблемой, и его «решение» - иногда лучшее решение состоит в том, чтобы знать, что его нет: P

Управляемый объект Core Data не видит связанные объекты до перезапуска Симулятора

...