2016-03-01 7 views
8

Я использую частный управляемый объектный контекст для создания некоторых новых объектов в постоянном хранилище, а затем после сохранения частного MOC, объединяя их в основной MOC, используя mergeChangesFromContextDidSaveNotification. Это отлично работает и обновляет пользовательский интерфейс по мере необходимости, а NSManagedObjectContextWillSaveNotification здесь НЕ используется для mainMOC.Как игнорировать изменения в mergeChangesFromContextDidSaveNotification в NSManagedObjectContextWillSaveNotification

Затем я вношу некоторые изменения в mainMOC с использованием пользовательского интерфейса и прослушиваю NSManagedObjectContextWillSaveNotification. Уведомление опубликовано, но оно содержит не только сделанные мной изменения, но и объекты, которые были объединены с PrivateMOC, используя mergeChangesFromContextDidSaveNotification.

Есть ли способ игнорировать изменения, которые были объединены из другого контекста в mainContext, после следующих уведомлений contextDidChange?

Вот установка:

- (void) loadData { 
    privateContext = [[NSManagedObjectContext alloc] initWithConcurrencyType: NSPrivateQueueConcurrencyType]; 

    privateContext.persistentStoreCoordinator = self.mainContext.persistentStoreCoordinator; 

    [[NSNotificationCenter defaultCenter] addObserver:self 
             selector:@selector(contextWillSave:) 
              name:NSManagedObjectContextWillSaveNotification 
              object: self.mainContext]; 

    NSManagedObject *object = [NSEntityDescription insertNewObjectForEntityForName:record.recordType inManagedObjectContext: self.privateContext]; 

    // fill in object 

    if ([self.privateContext hasChanges]) { 
     [self savePrivateContextAndMergeWithMainContext: self.privateContext]; 
    } 
} 

- (void) savePrivateContextAndMergeWithMainContext: (NSManagedObjectContext *) privateContext { 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(privateContextDidChange:) name:NSManagedObjectContextDidSaveNotification object:privateContext]; 
    __block NSError *error = nil; 
    [privateContext performBlockAndWait:^{ 
     NSLog(@"PrivateContext saved"); 
     [privateContext save:&error]; 
    }]; 


    [[NSNotificationCenter defaultCenter] removeObserver:self name:NSManagedObjectContextDidSaveNotification object:privateContext]; 

    if (error) { 
     NSLog(@"error = %@", error); 
    } 
} 

- (void) privateContextDidChange: (NSNotification *) notification{ 

    [self.mainContext performBlockAndWait:^{ 
     NSLog(@"merged into mainContext"); 
     [self.mainContext mergeChangesFromContextDidSaveNotification:notification]; 
    }]; 
} 

Это прекрасно работает и сохранение частного контекста и слияния в mainContext не вызывает уведомления contextWillSave. Но при редактировании данных из пользовательского интерфейса (на основном MOC) запускается уведомление и включает данные, которые ранее были сохранены с помощью частного MOC.

Надеюсь, что это ясно. Дайте мне знать, если я должен включить что-нибудь еще.

- ОБНОВЛЕНИЕ -

Похоже, что проблема с специально удаления объектов из частного контекста. После удаления из частного контекста и вызова mergeChangesFromContextDidSaveNotification на основном MOC набор deletedObjects mainMoc все еще показывает объект, который был удален. Это не происходит со вставками или обновлениями в частном контексте. Является ли это документированным где угодно? Каким может быть обходной путь?

+0

Почему вы также наблюдаете за основным контекстом? Что вы делаете с этими изменениями? – Wain

+0

Главный наблюдатель контекста должен слушать изменения, внесенные пользовательским интерфейсом, поэтому они могут быть переданы на сервер. Вот почему я не хочу, чтобы изменения в частном контексте отображались в основном слушателе контекста. Они не изначально, когда изменения объединяются в основной контекст, но похоже, что MOC по-прежнему отмечает их как «измененные», и при следующем сохранении основного контекста они отображаются в объекте уведомления. –

+0

На самом деле проблема, похоже, больше связана с объектами * deleted * в фоновом контексте, а не вставлена. Удаления - вот что вызывает у меня эту проблему, поскольку они, похоже, зависают после того, как «mergeChangesFromContextDidSaveNotification:» называется –

ответ

1

Основные данные существуют уже несколько лет, и за это время модель параллелизма развилась.

В моделях параллелизма «ограничение потока» и «блокировки» используются уведомления для обмена изменениями между контекстами. Когда контекст был сохранен, уведомление будет транслироваться с информацией об изменениях. Эта информация может использоваться для объединения изменений из одного контекста в другие. Это никогда не масштабировалось особенно хорошо и часто являлось серьезной проблемой для приложений. Модификации «блокировка» и «ограничение резьбы» устарели уже несколько лет.

Когда было введено «ограничение очереди», вместе с ним была введена концепция «вложенных контекстов». Контекст может иметь родительский контекст, и когда дочерний контекст сохраняется, эти изменения автоматически передаются родительскому (и не далее). Это рекомендуемый способ связывания изменений между контекстами при использовании ограничения по очереди, которым вы являетесь.

Не рекомендуется использовать mergeChangesFromContextDidSaveNotification: с контекстом NSPrivateQueueConcurrencyType.

Основные данные выполняют большое внутреннее ведение бухгалтерского учета и отслеживание изменений от вашего имени. Обычно во время операции сохранения информация агрегируется и объединяется - это часть операции processPendingChanges. Когда операция сохранения выполняется внутри performBlockAndWait:, это может не произойти или быть неполным, что приводит к тому, что изменения (и любое уведомление об изменении) являются неполными или вообще не происходят.performBlockAndWait: является реентерабельным, что несовместимо с процессом processPendingChanges. Использование performBlock: приведет к более последовательному поведению.

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

+0

«Не рекомендуется использовать mergeChangesFromContextDidSaveNotification: с контекстом NSPrivateQueueConcurrencyType». - У вас есть какая-то ссылка на это? Я видел много статей, в которых говорится об обратном; что в то время как контексты родитель-ребенок полезны в некоторых случаях, мы все равно должны использовать 'mergeChangesFromContextDidSaveNotification:' из частных контекстов, когда это необходимо. Я читал книгу основных данных Objc.io, и они используют 'mergeChangesFromContextDidSaveNotification' во многих местах. –

2

Изменение privateContextDidChange как это ...

- (void) privateContextDidChange: (NSNotification *) notification{ 
    if (notification.object == PrivateMOC) { 
     [self.mainContext performBlockAndWait:^{ 
      NSLog(@"merged into mainContext"); 
      [self.mainContext mergeChangesFromContextDidSaveNotification:notification]; 
     }]; 
    } 
} 

... где PrivateMOC является ссылкой на конфиденциальный Managed объект контекста?

+0

Это не помогает с проблемой. –

+0

Может не помочь в этом конкретном случае; но дает ответ на один (часть) вопросов: «Есть ли способ игнорировать изменения, которые были объединены из другого контекста [...]» :) –