如何处理临时NSManagedObject实例?

fsp*_*rit 85 iphone core-data objective-c

我需要创建NSManagedObject实例,用它们做一些事情,然后将它们丢弃或存储到sqlite db.问题是,我无法创建NSManagedObject未连接的实例,NSManagedObjectContext这意味着在我决定不需要我的数据库中的某些对象后,我必须以某种方式清理.

为了解决这个问题,我使用相同的协调器创建了一个内存存储,并且我使用assignObject:toPersistentStore.Now 将临时对象放在那里,如何确保这些临时对象无法获取数据,我从中获取数据两个商店的上下文共同点?或者我是否必须为此类任务创建单独的上下文?


UPD:

现在我正在考虑为内存存储创建单独的上下文.如何将对象从一个上下文移动到另一个上下文?只是使用[context insertObject:]?它在这个设置中可以正常工作吗?如果我从对象图中插入一个对象,整个图形是否也会插入到上下文中?

Mar*_*rra 144

注意:这个答案非常陈旧.查看完整历史记录的评论.我的建议已经改变,我不再建议使用无关联的NSManagedObject实例.我目前的建议是使用临时子NSManagedObjectContext实例.

原始答案

最简单的方法是创建NSManagedObject没有关联的实例NSManagedObjectContext.

NSEntityDescription *entity = [NSEntityDescription entityForName:@"MyEntity" inManagedObjectContext:myMOC];
NSManagedObject *unassociatedObject = [[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:nil];
Run Code Online (Sandbox Code Playgroud)

然后当你想保存它时:

[myMOC insertObject:unassociatedObject];
NSError *error = nil;
if (![myMoc save:&error]) {
  //Respond to the error
}
Run Code Online (Sandbox Code Playgroud)

  • 我不久前使用过这种方法,但在将这些对象插入MOC之前修改了这些对象和/或为它们创建了关系时,开始看到奇怪的行为和崩溃.我在WWDC上与一位核心数据工程师讨论了这个问题,他说虽然有非关联对象的API,但他强烈建议不要使用它,因为MOC严重依赖于其对象发送的KVO通知.他建议将常规NSObject用于临时对象,因为这样更安全. (40认同)
  • 这似乎不适用于iOS 8,特别是与持久关系.其他人可以证实吗? (7认同)
  • 如果unassociatedObject已经引用其他未关联的对象,我应该逐个插入它们还是myMOC足够智能收集所有引用并插入它们? (6认同)
  • 它足够聪明地处理关系. (6认同)
  • 我喜欢这种方法可以让你在决定存储MO之前将MO视为常规数据对象,但我担心CoreData合约的"支持"程度,以及它的未来性如何.苹果是否在任何地方提及或使用此方法?因为如果没有,未来的iOS版本可能会改变动态属性以依赖于MOC并打破这种方法.苹果文档对此并不清楚:他们强调上下文和指定的初始化程序的重要性,但是在MO文档中有一个提到"如果上下文不是nil,那么......"表明nil可能没问题 (2认同)
  • @AdrianSchönig那么我们应该将NSObject用于临时目的.不会复制我们的模型(一个用于临时目的,一个用于CoreDate目的.).有没有办法没有重复模型? (2认同)
  • 不,您应该使用托管对象,如果需要,可能在内存存储中.没有理由避免将Core Data用于临时对象.核心数据旨在**为您的模型,****可以**持续存在. (2认同)
  • @beretis当我七年前写这个答案时*我确实使用了没有上下文的`NSManagedObject`实例.我不再这样做,因为从那时起景观发生了巨大的变化.创建上下文不再昂贵,将"NSManagedObject"与上下文相关联不再昂贵.随着API的变化,建议会发生变化.如果您使用的是Core Data,则永远不会有理由创建数据传输对象. (2认同)

rai*_*ade 39

iOS5提供了一个更简单的替代Mike Weller的答案.而是使用 NSManagedObjectContext.它消除了通过NSNotificationCenter进行蹦床的需要

要创建子上下文:

NSManagedObjectContext *childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
childContext.parentContext = myMangedObjectContext;
Run Code Online (Sandbox Code Playgroud)

然后使用子上下文创建对象:

NSManagedObject *o = [NSEntityDescription insertNewObjectForEntityForName:@"MyObject" inManagedObjectContext:childContext];
Run Code Online (Sandbox Code Playgroud)

仅在保存子上下文时应用更改.所以放弃更改只是不保存.

关系仍有限制.即,您无法在其他上下文中创建与对象的关系.为了解决这个问题,请使用objectID来从子上下文中获取对象.例如.

NSManagedObjectID *mid = [myManagedObject objectID];
MyManagedObject *mySafeManagedObject = [childContext objectWithID:mid];
object.relationship=mySafeManagedObject;
Run Code Online (Sandbox Code Playgroud)

请注意,保存子上下文会将更改应用于父上下文.保存父上下文会保留更改.

有关完整说明,请参见wwdc 2012会话214.


Mik*_*ler 9

实现此类事情的正确方法是使用新的托管对象上下文.您使用相同的持久性存储创建托管对象上下文:

NSManagedObjectContext *tempContext = [[[NSManagedObjectContext alloc] init] autorelease];
[tempContext setPersistentStore:[originalContext persistentStore]];
Run Code Online (Sandbox Code Playgroud)

然后你添加新对象,改变它们等.

当需要保存时,需要在tempContext上调用[tempContext save:...],并处理保存通知以将其合并到原始上下文中.要丢弃对象,只需释放此临时上下文并忘记它.

因此,当您保存临时上下文时,更改将持久保存到商店,您只需将这些更改返回到主上下文中:

/* 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];
Run Code Online (Sandbox Code Playgroud)

这也是您应该处理多线程核心数据操作的方式.每个线程一个上下文.

如果您需要从这个临时上下文访问现有对象(添加关系等),那么您需要使用对象的ID来获取这样的新实例:

NSManagedObject *objectInOriginalContext = ...;
NSManagedObject *objectInTemporaryContext = [tempContext objectWithID:[objectInOriginalContext objectID]];
Run Code Online (Sandbox Code Playgroud)

如果您尝试NSManagedObject在错误的上下文中使用,则在保存时会出现异常.

  • "为此创建第二个上下文是非常浪费的,因为在内存和CPU中站立NSManagedObjectContext都很昂贵." .不,这不对.持久性存储协调器的依赖性(托管对象模型和具体存储)不是上下文.上下文很轻量级. (4认同)
  • @quellish同意.Apple在最近的WWDC核心数据性能会谈中表示,创建上下文非常轻量级. (3认同)
  • Apple仍在使用此技术(为CoreDataBooks示例代码创建第二个托管对象上下文). (2认同)

use*_*611 9

从nil上下文创建临时对象可以正常工作,直到您真正尝试与上下文的对象建立关系!= nil!

确保你没关系.


que*_*ish 8

你所描述的正是NSManagedObjectContext它的用途.

来自核心数据编程指南:核心数据基础

您可以将托管对象上下文视为智能便笺簿.从持久性存储中获取对象时,将临时副本放在便笺簿上,它们形成对象图(或对象图的集合).然后,您可以根据需要修改这些对象.但是,除非您实际保存这些更改,否则持久存储仍保持不变.

核心数据编程指南:托管对象验证

这也支持了表示"暂存区"的托管对象上下文的想法 - 通常,您可以将托管对象放在便笺簿上,然后根据您的意愿编辑它们,最后提交更改或丢弃它们.

NSManagedObjectContexts设计为轻量级.您可以随意创建和丢弃它们 - 它是持久性存储协调器,它的依赖性很"重".单个持久性存储协调器可以具有与之关联的许多上下文.在旧的,过时的线程限制模型下,这意味着在每个上下文中设置相同的持久性存储协调器.今天,它意味着将嵌套上下文连接到与持久性存储协调器关联的根上下文.

在该上下文中创建上下文,创建和修改托管对象.如果要保留它们并传达这些更改,请保存上下文.否则丢弃它.

尝试创建独立于a的托管对象NSManagedObjectContext是在寻找麻烦.请记住,核心数据最终是对象图的变更跟踪机制.因此,托管对象实际上是托管对象上下文的一部分.上下文观察它们的生命周期,如果没有上下文,并非所有托管对象功能都能正常工作.


gre*_*reg 6

根据您对临时对象的使用情况,上述建议有一些注意事项.我的用例是我想创建一个临时对象并将其绑定到视图.当用户选择保存此对象时,我想设置与现有对象的关系并保存.我想这样做是为了避免创建一个临时对象来保存这些值.(是的,我可以等到用户保存然后抓取视图内容,但我将这些视图放在一个表中,并且执行此操作的逻辑不太优雅.)

临时对象的选项是:

1)(首选)在子上下文中创建临时对象.这不起作用,因为我将对象绑定到UI,我无法保证在子上下文中调用对象访问器.(我没有找到其他文件,所以我不得不假设.)

2)使用nil对象上下文创建临时对象.这不起作用并导致数据丢失/损坏.

我的解决方案:我通过使用nil对象上下文创建临时对象来解决这个问题但是当我保存对象时,而不是将其作为#2插入,我将其所有属性复制到我在主上下文中创建的新对象中.我在我的NSManagedObject子类中创建了一个名为cloneInto的支持方法:它允许我轻松地为任何对象复制属性和关系.