在背景上下文中保存核心数据时出现NSInternalInconsistencyException

时间:2012-08-08 19:09:49

标签: ios multithreading core-data nsmanagedobjectcontext

我已经阅读了有关此错误的所有内容,但仍然无法确定为什么它会在我的应用中发生。

使用后台上下文保存多个Core Data对象时出现以下错误:

*** Terminating app due to uncaught exception "NSInternalInconsistencyException", reason: "Failed to process pending changes before save. The context is still dirty after 100 attempts. Typically this recursive dirtying is caused by a bad validation method, -willSave, or notification handler.

在下面的代码中,ArticleManager的{​​{1}}在主线程的循环中被调用。可能有0-200 +篇文章需要添加。此错误通常发生在文章计数100-150之间。

addArticle
//ArticleManager.m

-(id)init
{
    ... //normal init stuff
    dispatch_queue_t request_queue = dispatch_queue_create("com.app.articleRequest", NULL);
}    

-(void) addArticle:(Article *)article withURLKey:(NSString *)url
{
    //check if exists
    if ([downloadedArticles objectForKey:url] == nil && article != nil)
    {
        //add locally
        [downloadedArticles setObject:article forKey:url];

        //save to core data
        SaveArticle *saveArticle = [[SaveArticle alloc] init];
        [saveArticle saveArticle:article withURL:url onQueue:request_queue];
    }
}

2 个答案:

答案 0 :(得分:1)

好的,所以阅读官方文档有点用处。

来自Apple(强调我的):

  

并发

     

核心数据使用线程(或序列化队列)限制来保护   托管对象和托管对象上下文(请参阅“与...并发”   核心数据“)。这样做的结果是上下文假设了   默认所有者是分配它的线程或队列 - 这是   由调用其init方法的线程确定。 你不应该,   因此,在一个线程上初始化一个上下文然后将其传递给一个   不同的线程。相反,您应该传递对持久性的引用   存储协调器并使接收线程/队列创建一个新的   从中派生的上下文。如果使用NSOperation,则必须创建   main中的上下文(用于串行队列)或start(用于并发)   队列)。

所以我的问题是我在主线程上初始化了后台上下文,但后来通过dispatch_async使用了Grand Central Dispatch,它在后台线程上执行保存(使用在主线程上创建的上下文) )。

我通过将上下文初始化添加到后台块来修复它:

-(void)saveArticle:(Article *)article withURL:(NSString *)url onQueue:(dispatch_queue_t)queue
{       
    //save persistently in the background
    dispatch_async(queue, ^{

        NSManagedObjectContext *backgroundContext = [[NSManagedObjectContext alloc] init];
        [backgroundContext setPersistentStoreCoordinator:[managedObjectContext persistentStoreCoordinator]];

        ArticleCache *articleCacheObjectModel = (ArticleCache *)[NSEntityDescription insertNewObjectForEntityForName:@"ArticleCache" inManagedObjectContext:backgroundContext];

        if (article != nil)
        {
            [articleCacheObjectModel setArticleHTML:article.articleHTML];
            [articleCacheObjectModel setUrl:url];

            NSError *error;

            //Save the background context and handle the save notification 
            [[NSNotificationCenter defaultCenter] addObserver:self
                                                     selector:@selector(backgroundContextDidSave:)
                                                         name:NSManagedObjectContextDidSaveNotification
                                                       object:backgroundContext];

            if(![backgroundContext save:&error])
            {  
                //This is a serious error saying the record  
                //could not be saved. Advise the user to  
                //try again or restart the application.
            }

            [[NSNotificationCenter defaultCenter] removeObserver:self
                                                            name:NSManagedObjectContextDidSaveNotification
                                                          object:backgroundContext];
        }
    });
}

答案 1 :(得分:0)

是的,如果你使用限制并发模型(这是你用init得到的),那么你必须保证你只在创建它的线程中使用MOC。

您可以使用NSPrivateQueueConcurrencyType创建MOC,然后只需使用

[moc performBlock:^{
}];

执行操作。它有自己的内部队列,将在后台运行所有请求,使访问与其他调用同步。

您可以使用NSMainQueueConcurrencyType将MOC绑定到仅在主线程上运行。