Как бороться с временными экземплярами NSManagedObject? - PullRequest
85 голосов
/ 15 июля 2010

Мне нужно создать NSManagedObject экземпляры, сделать с ними что-нибудь и затем удалить их или сохранить в sqlite db. Проблема в том, что я не могу создать экземпляры NSManagedObject, не связанные с NSManagedObjectContext, и это означает, что мне нужно как-то прояснить ситуацию после того, как я решу, что мне не нужны некоторые объекты в моей БД.

Чтобы справиться с этим, я создал хранилище в памяти, используя тот же координатор, и помещаю туда временные объекты, используя assignObject:toPersistentStore. Теперь, как мне гарантировать, что эти временные объекты не попадут в данные что я получаю из общего для обоих магазинов контекста? Или я должен создать отдельные контексты для такой задачи?


UPD:

Теперь я думаю о создании отдельного контекста для хранилища в памяти. Как переместить объекты из одного контекста в другой? Просто используя [context insertObject:]? Будет ли это работать нормально в этой настройке? Если я вставлю один объект из графа объектов, весь граф также будет вставлен в контекст?

Ответы [ 8 ]

144 голосов
/ 15 июля 2010

ПРИМЕЧАНИЕ: Этот ответ очень старый. Смотрите комментарии для полной истории. С тех пор моя рекомендация изменилась, и я больше не рекомендую использовать неассоциированные NSManagedObject экземпляры. Моя текущая рекомендация - использовать временные дочерние NSManagedObjectContext экземпляры.

Оригинальный ответ

Самый простой способ сделать это - создать свои NSManagedObject экземпляры без связанных NSManagedObjectContext.

NSEntityDescription *entity = [NSEntityDescription entityForName:@"MyEntity" inManagedObjectContext:myMOC];
NSManagedObject *unassociatedObject = [[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:nil];

Затем, когда вы хотите сохранить его:

[myMOC insertObject:unassociatedObject];
NSError *error = nil;
if (![myMoc save:&error]) {
  //Respond to the error
}
39 голосов
/ 24 января 2013

iOS5 предоставляет более простую альтернативу ответу Майка Веллера. Вместо этого используйте child NSManagedObjectContext. Это устраняет необходимость батут через NSNotificationCenter

Чтобы создать дочерний контекст:

NSManagedObjectContext *childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
childContext.parentContext = myMangedObjectContext;

Затем создайте ваши объекты, используя дочерний контекст:

NSManagedObject *o = [NSEntityDescription insertNewObjectForEntityForName:@"MyObject" inManagedObjectContext:childContext];

Изменения применяются только при сохранении дочернего контекста. Так что отменить изменения просто не надо.

Существует еще ограничение на отношения. т.е. вы не можете создавать отношения с объектами в других контекстах. Чтобы обойти это, используйте objectID, чтобы получить объект из дочернего контекста. например.

NSManagedObjectID *mid = [myManagedObject objectID];
MyManagedObject *mySafeManagedObject = [childContext objectWithID:mid];
object.relationship=mySafeManagedObject;

Обратите внимание, что при сохранении дочернего контекста изменения применяются к родительскому контексту. Сохранение родительского контекста сохраняет изменения.

См. Сессия WWDC 2012 214 для полного объяснения.

9 голосов
/ 03 февраля 2012

Создание временных объектов из нулевого контекста работает нормально, пока вы на самом деле не попытаетесь установить связь с объектом, контекст которого! = Nil!

убедитесь, что с этим все в порядке.

9 голосов
/ 15 июля 2010

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

NSManagedObjectContext *tempContext = [[[NSManagedObjectContext alloc] init] autorelease];
[tempContext setPersistentStore:[originalContext persistentStore]];

Затем вы добавляете новые объекты, изменяете их и т. Д.

Когда приходит время сохранения, вам нужно вызвать [tempContext save: ...] в tempContext и обработайте уведомление о сохранении, чтобы объединить его с исходным контекстом.Чтобы отбросить объекты, просто освободите этот временный контекст и забудьте об этом.

Поэтому, когда вы сохраняете временный контекст, изменения сохраняются в хранилище, и вам просто нужно вернуть эти изменения в ваш основной контекст:

/* Called when the temp context is saved */
- (void)tempContextSaved:(NSNotification *)notification {
    /* Merge the changes into the original managed object context */
    [originalContext mergeChangesFromContextDidSaveNotification:notification];
}

// Here's where we do the save itself

// Add the notification handler
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(tempContextSaved:)
                                             name:NSManagedObjectContextDidSaveNotification
                                           object:tempContext];

// Save
[tempContext save:NULL];
// Remove the handler again
[[NSNotificationCenter defaultCenter] removeObserver:self
                                                name:NSManagedObjectContextDidSaveNotification
                                              object:tempContext];

Это также путьВы должны обрабатывать многопоточные операции с основными данными.Один контекст на поток.

Если вам нужен доступ к существующим объектам из этого временного контекста (для добавления связей и т. Д.), Вам нужно использовать идентификатор объекта, чтобы получить новый экземпляр, подобный этому:

NSManagedObject *objectInOriginalContext = ...;
NSManagedObject *objectInTemporaryContext = [tempContext objectWithID:[objectInOriginalContext objectID]];

Если вы попытаетесь использовать NSManagedObject в неправильном контексте, вы получите исключения при сохранении.

8 голосов
/ 15 августа 2014

То, что вы описываете, это именно то, для чего NSManagedObjectContext.

Из Руководство по программированию базовых данных: основные данные

Вы можете подуматьконтекста управляемого объекта как интеллектуальный блокнот.Когда вы выбираете объекты из постоянного хранилища, вы вносите временные копии на блокнот, где они образуют граф объектов (или коллекцию графов объектов).Затем вы можете изменить эти объекты так, как вам нравится.Однако, если вы не сохраните эти изменения, постоянное хранилище останется неизменным.

И Руководство по программированию основных данных: проверка управляемых объектов

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

NSManagedObjectContext с предназначены для легкого веса.Вы можете создавать и удалять их по своему усмотрению - это постоянный координатор магазинов и его зависимости "тяжелые".С одним постоянным координатором хранилища может быть связано много контекстов.В более старой, устаревшей модели ограничения потока это означало бы установку одного и того же постоянного координатора хранилища для каждого контекста.Сегодня это будет означать подключение вложенных контекстов к корневому контексту, связанному с координатором постоянного хранилища.

Создание контекста, создание и изменение управляемых объектов в этом контексте.Если вы хотите сохранить их и сообщить об этих изменениях, сохраните контекст.В противном случае откажитесь от него.

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

6 голосов
/ 09 февраля 2015

В зависимости от того, как вы используете временный объект, существуют некоторые предостережения в отношении приведенных выше рекомендаций. Мой вариант использования заключается в том, что я хочу создать временный объект и связать его с представлениями. Когда пользователь решает сохранить этот объект, я хочу установить отношения с существующими объектами и сохранить. Я хочу сделать это, чтобы избежать создания временного объекта для хранения этих значений. (Да, я мог бы просто подождать, пока пользователь сохранит, а затем захватить содержимое представления, но я помещаю эти представления в таблицу, и логика для этого менее элегантна.)

Опции для временных объектов:

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

2) Создать временный объект с нулевым контекстом объекта. Это не работает и приводит к потере / повреждению данных.

Мое решение: Я решил это путем создания временного объекта с нулевым контекстом объекта, но когда я сохраняю объект, а не вставляю его как # 2, я копирую все его атрибуты в новый объект, который я создаю в главном контексте. Я создал вспомогательный метод в своем подклассе NSManagedObject под названием cloneInto: он позволяет мне легко копировать атрибуты и отношения для любого объекта.

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

Для меня ответ Маркуса не сработал. Вот что сработало для меня:

NSEntityDescription entityForName:@"MyEntity" inManagedObjectContext:myMOC];
NSManagedObject *unassociatedObject = [[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:nil];

тогда, если я решу сохранить его:

[myMOC insertObject:unassociatedObjet];
NSError *error = nil;
[myMoc save:&error];
//Check the error!

Мы также не должны забыть выпустить его

[unassociatedObject release]
0 голосов
/ 17 ноября 2017

Я переписываю этот ответ для Swift, поскольку все подобные вопросы для быстрого перенаправления на этот вопрос.

Вы можете объявить объект без какого-либо ManagedContext, используя следующий код.1006 *

Позже, чтобы сохранить объект, вы можете вставить его в контекст и сохранить его.

myContext.insert(unassociatedObject)
// Saving the object
do {
    try self.stack.saveContext()
    } catch {
        print("save unsuccessful")
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...