我已经阅读了有关此错误的所有内容,但仍然无法确定为什么它会在我的应用中发生。
使用后台上下文保存多个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];
}
}
答案 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绑定到仅在主线程上运行。