6

我的情况:假设我有一个类Person(NSManagedObject 的子类)。每次用户单击按钮时,都会创建一个新的 Person 实例并将其添加到全局 NSMutableArray。新创建的 Person 实例也将被添加到子上下文中,如下所示:

NSManagedObjectContext *childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType: NSPrivateConcurrencyType];
[childContext setParentContext: _mainContext];

同样,当单击按钮时,我会保存上下文:(它有点复杂,但遵循这个结构)

[childContext performBlock:^{
    [childContext save:nil];
    [_mainContext save:nil];
}];

在我的数组中的按钮对象上单击两次或更多次(不确定是否取决于总点击次数)后变为fault.

根据文档:访问故障对象的属性应该加载持久对象。
即使当我访问我的 NSManagedObject 的属性时,该对象仍然是错误的并且属性是nil.

为什么我的数组中的对象出现故障,我如何访问故障对象的属性?

编辑

加载 UIViewController 时,我从数据存储区获取所有现有对象:

-(NSArray*)fetchPersons {
    NSManagedObjectContext *context = [self managedObjectContext]; //this is _mainContext, it is created with initWithConcurrencyType:NSMainQueueConcurrencyType
    NSFetchRequest  *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *description = [NSEntityDescription entityForName:@"Person" inManagedObjectContext:context];
    [fetchRequest setEntity:description];
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"key = %i", aCondition];
    [fetchRequest setPredicate:predicate];
    return [context executeFetchRequest:fetchRequest error:nil];
}

我正在使用这个 NSArrayfetchPersons来填充 NSMutableArray。

创建一个新的 Person 对象:

-(Person*)createPerson {
    NSManagedObjectContext *childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType: NSPrivateQueueConcurrencyType]; 
    [childContext setParentContext:[self managedObjectContext]];
    Person *person = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:childContext];
    return person;
} 

我不确定如何处理带有 òbjectID 的对象。我使用 childContext 作为临时上下文。有时我需要一个 Person 实例,但我不想将它保存到持久存储中(或者在开始时将其插入到主上下文中)。

在所有这些步骤之后,我的 NSMutableArray 中有所有对象。在创建一些对象并尝试记录它们的属性(person.name或其他东西)后,我得到了 nil 属性(故障对象)。

4

1 回答 1

20

NSManagedObjectNSManagedObjectContexts 与创建它们的对象保持紧密的关系。造成这种情况的具体原因是托管对象始终是可变的,因此可能需要写回存储区,并且预计未来会出现故障——Core Data 明确可能选择不将整个持久存储区加载到内存中,但可能必须处理未来任意遍历对象图或内存不足警告。

(除此之外:这也是 Core Data 对象不能用于创建它们的线程/队列以外的任何东西的最直接原因;它是上下文以及上下文和对象之间隐式通信的各种位'安全)

这在实践中意味着您不应该允许托管对象超过其存储。

为了允许您在具有不同上下文的不同代理之间来回发送对象,Apple 实现了NSManagedObjectID它是任何托管对象的唯一标识符。这是一个完全不透明的类,但作为参考,如果你有一个 SQLite 存储,那么它就是对相关表和行的引用;如果您有其他商店类型之一,那么它同样是指向商店中某个位置的指针。它本身不携带任何对象数据。

所以你通常做的是objectID在创建它的上下文仍然存在时调用一个对象。然后,您可以将其传递给其他想要它的人。然后,他们将使用[myManagedObjectContext -existingObjectWithID:error:]来获取他们可以安全使用的托管对象的新副本。新副本将绑定到该上下文而不是原始副本,因此无论何时何地该上下文是安全的,而不是原始的,它都是安全的。

唯一可能令人惊讶的是,当您第一次插入一个对象时,它只会获得一个临时对象 ID。这是因为 Core Data 喜欢将需要插入到存储中的东西批量化,然后仅在您请求保存时才将它们全部插入。

出于您的目的,您不希望在保存之前传递 ID,因为该对象不会存在于父存储中。因此,这一点部分是学术性的,但假设您出于其他原因想要提取 ID,您还可以考虑使用 context 的-obtainPermanentIDsForObjects:error:,这可能比实际保存快很多,具体取决于您的商店类型,并且绝对不会慢点。

于 2013-02-07T20:19:45.047 回答