Стратегия разработки для сохранения в фоновом режиме с помощью MagicalRecord

Недавно я запустил новое приложение, требующее только одного магазина (без приложения на основе документов). Некоторое время я был вполне счастлив, думая, что наконец-то смогу избавиться от разбрасывания NSManagedObjectContext... пока не захотел сохранить в фоновом режиме :-(

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

- (void)awakeFromInsert
{
    [super awakeFromInsert];
    [self resetCard];
    self.creationDate = TODAY;
    self.dictionary = [Dictionary activeDictionary];
    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
    [center postNotificationName:NOTE_NEWCARD object:self];    
}

[Dictionary activeDictionary] — это статическая функция NSManagedObject, возвращающая указатель на NSManagedObject, созданный в основном потоке. Это вызовет перекрестную/контекстную ошибку во время фонового сохранения. Поскольку моя программа всегда читала из одного и того же хранилища, я подумал, что смогу избежать написания этого:

[Dictionary activeDictionaryWithContext:...]

Я полагаю, что с MagicalRecord, пока я всегда работаю с одним и тем же бэкэндом, можно избежать передачи указателя контекста. Какую функцию следует использовать для получения этого контекста?

[NSManagedObjectContext MR_defaultContext]
[NSManagedObjectContext MR_context]
[NSManagedObjectContext MR_contextForCurrentThread]

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

  • В случае уведомления я должен всегда отправлять только objectID?

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

  • Могу ли я безопасно пропустить два проблемных вызова функций, о которых я упоминал, если я сохраняю с помощью [MagicalRecord MR_saveAll]?

  • Должен ли я предположить, что объекты нового контекста фонового сохранения будут точной копией объектов в моем основном потоке без вызова этих дополнительных функций?

Теперь у меня проблемы, потому что я никогда не ожидал, что awakeFromInsert будет запускаться несколько раз для одного и того же объекта в одном и том же хранилище. Я думал о чем-то вроде этого:

- (void)awakeFromInsert
{
    [super awakeFromInsert];
    if ([self managedObjectContext] == [NSManagedObjectContext MR_defaultContext]) {
        [self resetCard];
        self.creationDate = TODAY;
        self.dictionary = [Dictionary activeDictionary];
        NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
        [center postNotificationName:NOTE_NEWCARD object:self];    
    }
}

Это должно заставить мой код awakeFromInsert запускаться только один раз, но не в контексте фонового сохранения. Я боюсь потерять информацию, если я это сделаю


person SystematicFrank    schedule 03.11.2012    source источник


Ответы (1)


Хотя вы, безусловно, можете отправить свой объект в уведомлении таким образом, я бы рекомендовал против этого. Помните, что даже с новыми контекстами родитель-потомок в CoreData NSManagedObjects НЕ потокобезопасны. Если вы создаете или импортируете объекты, вам необходимо сохранить их перед использованием в другом контексте.

MagicalRecord предоставляет относительно простой API для фонового сохранения:

[MagicalRecord saveInBackgroundWithBlock:^(NSManagedObjectContext *localContext){
    MyEntity *newEntity = [MyEntity MR_createInContext:localContext];

    //perform other entity operations here
}];

Этот блок делает всю работу за вас, не беспокоясь о правильной настройке NSManagedObjectContext.

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

Еще одна альтернатива представленному вами подходу к уведомлению – добавить наблюдателя в NSManagedObjectContextDidSaveNotification и объединить ваши изменения в этом уведомлении. Это сработает только после того, как ваши объекты будут сохранены, и безопасно для пересечения контекстов либо через отношения родитель-потомок, либо через постоянное хранилище (старый способ).

person casademora    schedule 03.11.2012
comment
Спасибо, я использую MagicalRecord 2.0.7, и хотя я работаю только с одним контекстом, простой MR_save создает некоторые проблемы, поскольку работает со вторым созданным им контекстом. Хуже всего то, что уведомление пришло из третьего контекста?!?!? - person SystematicFrank; 03.11.2012
comment
Я знаю, что NSManagedObjects не являются потокобезопасными, моя проблема заключается в том, как мне с этим справиться и что происходит во время MR_save. Я вижу, что создается второй контекст, поэтому мой awakeFromInsert вызывается дважды для объектов одного и того же хранилища. Меня беспокоит потеря информации, если мой код awakeFromInsert выполняется только в том случае, если текущий контекст является основным потоком. Обновление вопроса сейчас... - person SystematicFrank; 03.11.2012
comment
awakeFromInsert вызывается дважды, потому что вы создаете два экземпляра в разных контекстах. Я думаю, что вы хотите создать объект, затем сохранить, а затем загрузить объект во втором контексте. - person casademora; 03.11.2012
comment
Через два дня после этого поста я обнаружил, что должен написать условие, чтобы исключить awakeFromInsert, если он не работает в текущем контексте. Я совершенно уверен, что работаю только с одним единственным контекстом. MR_save создает новый и воссоздает экземпляры всех моих объектов, поэтому дважды выполняет все мои файлы awakeFromInsert. Использование стандартного :save:&error не вызывает второй вызов. - person SystematicFrank; 06.11.2012
comment
Слияние контекстов обрабатывается Magic? Или мне нужно настроить AppDelegate, чтобы справиться с этим? - person Johan Karlsson; 17.03.2014