托管对象上下文未保存到持久存储

Posted

技术标签:

【中文标题】托管对象上下文未保存到持久存储【英文标题】:Managed Object Context not saving to persistant store 【发布时间】:2012-02-03 18:00:21 【问题描述】:

我有一个线程操作,它创建一个新的托管对象,将其保存到持久存储中,然后通过 NSNotification 将新对象的 objectID 传递给主线程以进行进一步处理

但是,当我尝试从主线程访问新创建的托管对象时,我在后台线程上设置的所有值都返回为空。

** 后台线程

// create a new managed object context for this thread  
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];  
[context setPersistentStoreCoordinator:[appDelegate persistentStoreCoordinator]];
[context setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];

// create the object
MyObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:@"MyObject" inManagedObjectContext:context];  
[newManagedObject setAValue:@"A"];  
[newManagedObject setBValue:@"B"];  
[newManagedObject setCValue:@"C"];  

// save it on the main thread
[context performSelectorOnMainThread:@selector(save:) withObject:nil waitUntilDone:NO];

// post notification to main thread, pass the objectID
NSMutableDictionary *userInfo = [NSDictionary dictionaryWithObject:[newManagedObject objectID] forKey:@"objectID"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"doneInsertingObject" object:userInfo];  
[context release];

** 主线程

...
// register notification for background thread
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mergeContextChanges:) name:NSManagedObjectContextDidSaveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(doSomethingWithObject:) name:@"doneInsertingObject" object:nil];
...

 - (void)doSomethingWithObject:(NSNotification*)noif

    if([NSThread isMainThread] == NO)
    
        // run this on the main thread
        [self performSelectorOnMainThread:_cmd withObject:noif waitUntilDone:NO];
        return;
    

    // get managed object from objectID
    NSDictionary *userInfo = [noif userInfo];
    MyObject *object = (MyObject*)[appDelegate.managedObjectContext objectWithID:[userInfo valueForKey:@"objectID"]];
    [appDelegate.managedObjectContext refreshObject:object mergeChanges:YES];

    // these should return 'A, B, C' but all three return 'nil'
    NSLog(@"aValue: %@", object.aValue);
    NSLog(@"bValue: %@", object.bValue);
    NSLog(@"cValue: %@", object.cValue);


// merge background thread moc with main moc
- (void)mergeContextChanges:(NSNotification *)notification

    if([NSThread isMainThread] == NO)
    
        // run this on the main thread
        [self performSelectorOnMainThread:_cmd withObject:notification waitUntilDone:NO];
        return;
    

    // fault all updated objects
    NSSet *updated = [[notification userInfo] objectForKey:NSUpdatedObjectsKey];
    for(NSManagedObject *thing in updated)
    
        [[appDelegate.managedObjectContext objectWithID:[thing objectID]] willAccessValueForKey:nil];
    

    // merge changes to the main managed object context
    [appDelegate.managedObjectContext mergeChangesFromContextDidSaveNotification:notification];

    // force processing of any pending changes
    [appDelegate.managedObjectContext processPendingChanges];

我已尝试更改合并策略,但没有任何区别。

我已尝试将日志记录添加到上下文合并方法中,并且已确认在调用主线程上的 doSomethingWithObject: 方法之前收到了来自后台线程的“插入”通知。

为什么我的数据没有更新到持久存储?

【问题讨论】:

【参考方案1】:

我看不到您为后台线程保存上下文的位置。如果是这一行

// save it on the main thread
[context performSelectorOnMainThread:@selector(save:) withObject:nil waitUntilDone:NO];

我不知道它是否正确。您已经从创建它的线程中保存了上下文,而不是在主线程中。

[context save:&error];

有关更多信息,我建议您阅读 Marcus Zarra importing-and-displaying-large-data-sets-in-core-data 的文章。您可以在末尾找到示例代码。此外,您可以在using-core-data-on-multiple-threads 找到更多信息。

希望对你有帮助。

【讨论】:

实际上,如果您查看developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/…,它会提到“在后台线程中保存容易出错”,如果可能的话应该在主线程中完成。 合并上下文时是否尝试过[self performSelectorOnMainThread:_cmd withObject:noif waitUntilDone:YES]; 将 waitUntilDone: 设置为“YES”确实解决了我正在使用的代码的问题。但是,我有更多关于调用 save: 是否可以/不可以的具体信息:来自后台线程。如果这完全没问题,我宁愿那样做。 @bandejapaisa 我做到了。但苹果文档似乎并不清楚在后台与前台保存 MOC 的主题。 “在后台线程中保存容易出错”部分有点令人困惑,因为它已经声明不要跨线程使用 MOC。为什么 Apple 文档必须重复同样的事情,为什么有人想首先将主 MOC 保存在后台线程上(Apple 在上面的文本中声明不要这样做)?【参考方案2】:

您的NSManagedObjectContext 必须保存在创建它的线程上(正如@Flex_Addicted 所思考的那样)。

在后台线程上保存后,将发布一条通知,告诉您将后台上下文中的更改合并到主上下文中。

Apple 的文档显示“保存在后台线程中容易出错”——这与使用另一个 NSManagedObjectContext 无关。他们说,如果您有 1 个上下文,并且您尝试将保存操作分派到后台 - 这很容易出错。如果您有多个上下文,则它们一次只能在一个线程中使用。

【讨论】:

以上是关于托管对象上下文未保存到持久存储的主要内容,如果未能解决你的问题,请参考以下文章

一个托管对象上下文的两个持久存储 - 可能吗?

区分从内存中托管对象上下文和持久存储中获取的对象

RestKit/CoreData 没有立即将关系保存到持久存储

两个托管对象上下文可以共享一个持久存储协调器吗?

设备密码锁定时无法保存核心数据托管对象上下文

保存托管对象上下文会在 iOS 5 的 performBlock 中创建死锁