Как работать с временными экземплярами NSManagedObject?

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

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


UPD:

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

Ответ 1

Самый простой способ сделать это - создать экземпляры 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
}

Ответ 2

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 session 214.

Ответ 3

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

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 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 в неправильном контексте, вы получите исключения при сохранении.

Ответ 4

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

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

Ответ 5

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

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

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

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

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

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

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

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

Ответ 6

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

Параметры для временных объектов:

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

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

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

Ответ 7

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

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]

Ответ 8

Я переписываю этот ответ для 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")
    }
}