Основные данные: Как объединить вставки / обновления / удаления между двумя NSManagedObjectContext, поддерживая объединение как отменяемый шаг? - PullRequest
2 голосов
/ 01 июля 2011

У меня есть основанное на документе приложение Core Data (работающее в Mac OS X 10.5 и выше), где я пытаюсь использовать два NSManagedObjectContext в главном потоке.Я хотел бы объединить изменения, сделанные во вторичном контексте, с моим основным (основным) контекстом.Кроме того, я хочу, чтобы изменения, которые были объединены из вторичного контекста, были отменены и чтобы документ был помечен как «грязный».Я думаю, мой вопрос похож на « Отмена вставки базовых данных, которые выполняются из основного потока », но, ATM, я не использую другие потоки.

Я наблюдал заNSManagedObjectContextDidSaveNotification (который отправляется из второго контекста при вызове -[self.secondaryContext save:]) следующим образом:

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(mocDidSave:)
                                             name:NSManagedObjectContextDidSaveNotification
                                           object:self.secondaryContext];

В методе -mocDidSave:, вызванном наблюдателем, я пытался использовать -[NSManagedObjectContext mergeChangesFromContextDidSaveNotification:] в основном контекстечтобы объединить изменения из вторичного контекста в первичный контекст:

- (void)mocDidSave:(NSNotification *)notification
{
    [self.primaryContext mergeChangesFromContextDidSaveNotification:notification];
}

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

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

- (void)mocDidSave:(NSNotification *)notification
{
    [self.primaryContext mergeChangesFromContextDidSaveNotification:notification];

    for (NSManagedObject *insertedObject in [[notification userInfo] objectForKey:NSInsertedObjectsKey]) {
        [self.primaryContext refreshObject:[self.primaryContext existingObjectWithID:[insertedObject objectID] error:NULL] mergeChanges:NO];
    }
}

Wrt-mocDidSave:, у меня были несколько лучшие результаты с пользовательской реализацией:

- (void)mocDidSave:(NSNotification *)notification
{
    NSDictionary *userInfo = [notification userInfo];

    NSSet *insertedObjects = [userInfo objectForKey:NSInsertedObjectsKey];
    if ([insertedObjects count]) {
        NSMutableArray *newObjects = [NSMutableArray array];
        NSManagedObject *newObject = nil;
        for (NSManagedObject *insertedObject in insertedObjects) {
            newObject = [self.primaryContext existingObjectWithID:[insertedObject objectID] error:NULL];
            if (newObject) {
                [self.primaryContext insertObject:newObject];
                [newObjects addObject:newObject];
            }
        }

        [self.primaryContext processPendingChanges];

        for (NSManagedObject *newObject in newObjects) {
            [self.primaryContext refreshObject:newObject mergeChanges:NO];
        }
    }

    NSSet *updatedObjects = [userInfo objectForKey:NSUpdatedObjectsKey];
    if ([updatedObjects count]) {
        NSManagedObject *staleObject = nil;
        for (NSManagedObject *updatedObject in updatedObjects) {
            staleObject = [self.primaryContext objectRegisteredForID:[updatedObject objectID]];
            if (staleObject) {
                [self.primaryContext refreshObject:staleObject mergeChanges:NO];
            }
        }
    }

    NSSet *deletedObjects = [userInfo objectForKey:NSDeletedObjectsKey];
    if ([deletedObjects count]) {
        NSManagedObject *staleObject = nil;
        for (NSManagedObject *deletedObject in deletedObjects) {
            staleObject = [self.primaryContext objectRegisteredForID:[deletedObject objectID]];
            if (staleObject) {
                [self.primaryContext deleteObject:staleObject];
            }
        }

        [self.primaryContext processPendingChanges];
    }
}

Это приводит к обновлению контроллера массива, помечению документа как грязному, а вставки и удаления отменяются.Тем не менее, у меня все еще есть проблемы с обновлениями, которые еще нельзя отменить.Должен ли я вместо этого вручную зацикливать все обновленные объекты и использовать -[NSManagedObject changedValues] для повторного применения изменений в первичном контексте?

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

1 Ответ

1 голос
/ 01 июля 2011

Если вы не используете отдельные потоки, то вам фактически не нужно разделять контексты. Использование двух контекстов в одном потоке добавляет сложности, ничего не получая. Если вы точно не знаете, что будете использовать потоки, я настоятельно рекомендую использовать только один контекст. Преждевременная оптимизация - корень всего зла.

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

Единственный способ сделать это, чтобы получить неограниченную отмену, - это сохранить несколько версий документа за кулисами. IIRC, вы также можете сериализовать менеджер отмены, чтобы его можно было записать в файл и загрузить обратно.

...