Как работать с временными экземплярами NSManagedObject?
мне нужно создать NSManagedObject
экземпляры, сделать некоторые вещи с ними, а затем мусор их или хранить в SQLite db. Проблема в том, что я не могу создавать экземпляры NSManagedObject
связано с NSManagedObjectContext
и это означает, что я должен как-то прояснить после того, как я решу, что мне не нужны некоторые объекты в моей БД.
чтобы справиться с этим, я создал хранилище в памяти, используя тот же координатор, и я помещаю там временные объекты с помощью assignObject:toPersistentStore.
Теперь, как я могу гарантировать, что эти временные объекты не попадают в данные, которые я получаю из общего для обоих магазинов контекста? Или мне нужно создать отдельные контексты для такой задачи?
UPD:
теперь я думаю о создании отдельного контекста для хранения в памяти. Как переместить объекты из одного контекста в другой? Просто используя [context insertObject:]? Будет ли он работать нормально в этой настройке? Если я вставляю один объект из графика объектов, весь график также вставляется в контекст?
8 ответов:
Примечание: ответ очень старый. См. комментарии для полной истории. С тех пор моя рекомендация изменилась, и я больше не рекомендую использовать unassociated
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 }
iOS5 предоставляет более простую альтернативу ответу Майка Веллера. Вместо этого используйте ребенок 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 для полного объяснения.
правильный способ достижения такого рода вещей - это новый контекст управляемого объекта. Вы создаете контекст управляемого объекта с тем же постоянным хранилищем:
NSManagedObjectContext *tempContext = [[[NSManagedObjectContext alloc] init] autorelease]; [tempContext setPersistentStore:[originalContext persistentStore]];
затем вы добавляете новые объекты, видоизменить и т. д.
когда приходит время, чтобы сэкономить, вам нужно позвонить [tempContext сохранить:...] в 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
в неправильном контексте вы получите исключения при сохранении.
создание временных объектов из контекста nil отлично работает, пока вы на самом деле не попытаетесь установить отношения с объектом, контекст которого != ноль!
убедитесь, что вы в порядке с этим.
то, что вы описываете, это именно то, что
NSManagedObjectContext
для.С Руководство По Программированию Основных Данных: Основы Основных Данных
вы можете думать о контексте управляемого объекта как умный Блокнот. Когда вы извлекаете объекты из постоянного хранилища, вы приносите временные копии на Блокнот, где они образуют граф объектов (или коллекцию графов объектов). Затем вы можете изменить эти объекты, как вам нравится. Если вы на самом деле не сохраните эти изменения, однако, постоянное хранилище остается неизменным.
и Руководство По Программированию Основных Данных: Проверка Управляемых Объектов
Это также лежит в основе идеи контекста управляемого объекта, представляющего "скретч-пэд" - в общем случае вы можете принести управляемые объекты на скретч-пэд и редактировать их, как вы хотите, прежде чем в конечном итоге либо фиксировать изменения, либо отбрасывать их.
NSManagedObjectContext
s предназначены для легкий. Вы можете создавать и отбрасывать их по своему желанию - это постоянный координатор магазинов, и это зависимости, которые являются "тяжелыми". Один постоянный координатор хранилища может иметь много контекстов, связанных с ним. В более старой, устаревшей модели ограничения потока это означало бы установку одного и того же постоянного координатора хранилища в каждом контексте. Сегодня это будет означать подключение вложенных контекстов к корневому контексту, который связан с координатором постоянного хранилища.создать контексте, создание и изменение управляемых объектов в этом контексте. Если вы хотите сохранить их и передать эти изменения, сохраните контекст. В противном случае отбросьте его.
попытка создать управляемые объекты, независимые от
NSManagedObjectContext
напрашивается на неприятности. Помните, что основные данные в конечном счете являются механизмом отслеживания изменений для графа объектов. Из-за этого, управляемые объекты действительно часть контекста управляемого объекта. Контекст наблюдает за их жизнью цикл и без контекста Не все функции управляемого объекта будут работать правильно.
в зависимости от вашего использования временного объекта есть некоторые предостережения, приведенные выше рекомендации. Мой вариант использования заключается в том, что я хочу создать временный объект и связать его с видом. Когда пользователь решает сохранить этот объект, я хочу настроить отношения с существующими объектами и сохранить. Я хочу сделать это, чтобы избежать создания временного объекта для хранения этих значений. (Да, я мог бы просто подождать, пока пользователь сохранит, а затем захватить содержимое представления, но я помещаю эти представления внутри таблицы и логика для этого менее элегантным.)
параметры временного объекта:
1) (предпочтительно) создайте временный объект в дочернем контексте. Это не будет работать, потому что я привязываю объект к пользовательскому интерфейсу, и я не могу гарантировать, что методы доступа к объекту вызываются в дочернем контексте. (Я не нашел никакой документации, в которой говорится иначе, поэтому я должен предположить.)
2) Создайте временный объект с нулевым контекстом объекта. Это не работает и приводит к данным потеря/повреждение.
Мое Решение: Я решил это, создав временный объект с нулевым контекстом объекта, но когда я сохраняю объект, а не вставляю его как #2, я копирую все его атрибуты в новый объект, который я создаю в основном контексте. Я создал вспомогательный метод в своем подклассе NSManagedObject под названием cloneInto: это позволяет мне легко копировать атрибуты и отношения для любого объекта.
для меня ответ Маркуса не сработал. Вот что сработало для меня:
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]
я переписываю этот ответ для Swift, как и все подобные вопросы для swift перенаправления на этот вопрос.
вы можете объявить объект без какого-либо ManagedContext, используя следующий код.
let entity = NSEntityDescription.entity(forEntityName: "EntityName", in: myContext) let unassociatedObject = NSManagedObject.init(entity: entity!, insertInto: nil)
позже, чтобы сохранить объект, вы можете вставить его в контекст и сохранить его.
myContext.insert(unassociatedObject) // Saving the object do { try self.stack.saveContext() } catch { print("save unsuccessful") } }