Как мне совместно использовать хранилище Core Data между процессами, использующими NSDistributedNotifications? - PullRequest
8 голосов
/ 29 ноября 2009

Фон

Я уже разместил вопрос об основах совместного использования основного хранилища данных между процессами .

Я пытаюсь выполнить приведенные рекомендации, и у меня возникают проблемы.

Моя цель

У меня есть два процесса - приложение-помощник и пользовательский интерфейс. Они оба используют одно хранилище данных. Я хочу, чтобы пользовательский интерфейс обновил свой NSManagedObjectContext, когда приложение Helper App сохранит новые данные в хранилище.

Текущий поток программы

  1. Вспомогательный процесс приложения записывает данные в хранилище.

  2. В приложении Helper я прослушиваю уведомления NSManagedObjectContextDidSaveNotification.

  3. Когда контекст сохранен, я кодирую вставленные, удаленные и обновленные объекты, используя их представления URI и NSArchiver.

  4. Я отправляю NSNotification в NSDistributedNotificationCenter с этим закодированным словарем в качестве userInfo.

  5. Процесс пользовательского интерфейса прослушивает уведомление о сохранении. Когда он получает уведомление, он разархивирует userInfo, используя NSUnarchiver.

  6. Он просматривает все обновленные / вставленные / удаленные объекты из данных URI и заменяет их NSManagedObjects.

  7. Создает NSNotification с обновленными / вставленными / удаленными объектами.

  8. Я вызываю mergeChangesFromContextDidSaveNotification: в контексте управляемого объекта процесса пользовательского интерфейса, передавая NSNotification, который я создал на предыдущем шаге.

Проблема

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

Что я пробовал

  1. Самая очевидная вещь, которую можно попробовать быть, чтобы передать уведомление о сохранении от вспомогательного процесса приложения к Процесс пользовательского интерфейса. Легко, правда? Ну нет. Распределенные уведомления не будут позвольте мне сделать это как userInfo словарь не в нужном формат. Вот почему я делаю все NSArchiving вещи.

  2. Я пытался позвонить refreshObject: mergeChanges: YES on NSManagedObjects для обновления, но это, похоже, не имеет никакого эффект.

  3. Я пытался выполнить mergeChangesFromContextDidSaveNotification: селектор в основной теме и текущая тема. Ни то, ни другое влияет на результат.

  4. Я пытался использовать mergeChangesFromContextDidSaveNotification: перед тем как Конечно, намного проще, и это сработало в совершенстве. Но мне нужно это же функциональность между процессами.

Альтернативы

Я что-то здесь упускаю? У меня постоянно возникает ощущение, что я делаю это намного сложнее, чем нужно, но после прочтения документации несколько раз и потраченных на это нескольких дней, я не вижу другого способа обновить MOC пользовательский интерфейс.

Есть ли более элегантный способ сделать это? Или я просто делаю глупую ошибку где-то в моем коде?

Код

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

Код приложения Helper

   -(void)workerThreadObjectContextDidSave:(NSNotification *)saveNotification {
        NSMutableDictionary *savedObjectsEncodedURIs = [NSMutableDictionary dictionary];
        NSArray *savedObjectKeys = [[saveNotification userInfo] allKeys];

        for(NSString *thisSavedObjectKey in savedObjectKeys) {
            // This is the set of updated/inserted/deleted NSManagedObjects.
            NSSet *thisSavedObjectSet = [[saveNotification userInfo] objectForKey:thisSavedObjectKey];
            NSMutableSet *thisSavedObjectSetEncoded = [NSMutableSet set];

            for(id thisSavedObject in [thisSavedObjectSet allObjects]) {
                // Construct a set of URIs that will be encoded as NSData
                NSURL *thisSavedObjectURI = [[(NSManagedObject *)thisSavedObject objectID] URIRepresentation];
                [thisSavedObjectSetEncoded addObject:thisSavedObjectURI];
            }
            // Archive the set of URIs.
            [savedObjectsEncodedURIs setObject:[NSArchiver archivedDataWithRootObject:thisSavedObjectSetEncoded] forKey:thisSavedObjectKey];
        }

        if ([[savedObjectsEncodedURIs allValues] count] > 0) {
            // Tell UI process there are new objects that need merging into it's MOC
            [[NSDistributedNotificationCenter defaultCenter] postNotificationName:@"com.synapticmishap.lapsus.save" object:@"HelperApp" userInfo:(NSDictionary *)savedObjectsEncodedURIs];
        }
    }

Код интерфейса пользователя

-(void)mergeSavesIntoMOC:(NSNotification *)notification {
    NSDictionary        *objectsToRefresh        = [notification userInfo];
    NSMutableDictionary *notificationUserInfo    = [NSMutableDictionary dictionary];
    NSArray *savedObjectKeys = [[notification userInfo] allKeys];

    for(NSString *thisSavedObjectKey in savedObjectKeys) {
        // Iterate through all the URIs in the decoded set. For each URI, get the NSManagedObject and add it to a set.
        NSSet *thisSavedObjectSetDecoded = [NSUnarchiver unarchiveObjectWithData:[[notification userInfo] objectForKey:thisSavedObjectKey]];
        NSMutableSet *savedManagedObjectSet = [NSMutableSet set];

        for(NSURL *thisSavedObjectURI in thisSavedObjectSetDecoded) {
            NSManagedObject *thisSavedManagedObject = [managedObjectContext objectWithID:[persistentStoreCoordinator managedObjectIDForURIRepresentation:thisSavedObjectURI]];
            [savedManagedObjectSet addObject:thisSavedManagedObject];
            // If the object is to be updated, refresh the object and merge in changes.
            // This doesn't work!
            if ([thisSavedObjectKey isEqualToString:NSUpdatedObjectsKey]) {
                [managedObjectContext refreshObject:thisSavedManagedObject mergeChanges:YES];
                [managedObjectContext save:nil];
            }
        }
        [notificationUserInfo setObject:savedManagedObjectSet forKey:thisSavedObjectKey];
    }
    // Build a notification suitable for merging changes into MOC.
    NSNotification *saveNotification = [NSNotification notificationWithName:@"" object:nil userInfo:(NSDictionary *)notificationUserInfo];
    [managedObjectContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:)
                                    withObject:saveNotification
                                 waitUntilDone:YES];
}

Ответы [ 6 ]

2 голосов
/ 28 января 2011

Я использовал метод в

http://www.mlsite.net/blog/?p=518

тогда каждый объект корректно ошибается, но ошибки выбираются в кеше, поэтому по-прежнему нет обновления

Я должен был сделать [moc stalenessInterval = 0];

И, наконец, это сработало с отношениями.

1 голос
/ 07 декабря 2011

Это работает, за исключением приложений для песочниц. Вы не можете отправить уведомление с информацией о пользователе. Вместо этого рассмотрим другой IPC, такой как XPC или DO.

Кстати, использование NSDustributedNotificationCenter не всегда составляет 100%, если система занята.

1 голос
/ 10 марта 2010

У меня была точно такая же проблема с приложением для iPhone, над которым я работал. В моем случае решение состояло в том, чтобы установить для stalenessInterval Context значение, равное бесконечно малому (например, 0,5 секунды).

1 голос
/ 13 декабря 2009

Я бы пошел с предложением Майка и просто посмотрел файл магазина на предмет изменений.

Хотя это может быть не самым эффективным, я успешно использовал - [NSManagedObjectContext reset] из второго процесса, когда происходит изменение в магазине. В моем случае код довольно линейный & mdash; все, что я делаю, это запускаю запрос на выборку для некоторых данных после сброса. Я не знаю, как это будет работать с привязками и сложным пользовательским интерфейсом, но, возможно, вы сможете опубликовать уведомление, чтобы обновить вещи вручную, если оно не обрабатывается автоматически.

1 голос
/ 10 декабря 2009

Вы ищете - (void) refreshObject: (NSManagedObject *) объект mergeChanges: (BOOL) флаг, который я считаю.

Это обновит объект с информацией в постоянном хранилище, объединяя изменения, если хотите.

0 голосов
/ 02 сентября 2010

Настройка stalenessInterval контекста управляемого объекта работает. Мой случай включает в себя несколько потоков вместо процесса, хотя.

...