[英]NSInternalInconsistencyException when saving Core Data on Background Context
我已經閱讀了有關此錯誤的所有內容,但仍然無法確定為什么它會在我的應用程序中發生。
使用后台上下文保存多個核心數據對象時出現以下錯誤:
*** 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
的addArticle
被稱為在主線程上的環。 可能需要添加0-200篇以上的文章。 此錯誤通常發生在商品數100-150之間。
//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];
}
}
//SaveArticle.m
@implementation SaveArticle
@synthesize managedObjectContext;
@synthesize backgroundContext;
-(id)init
{
if (![super init]) return nil;
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
managedObjectContext = [appDelegate managedObjectContext];
backgroundContext = [[NSManagedObjectContext alloc] init];
[backgroundContext setPersistentStoreCoordinator:[managedObjectContext persistentStoreCoordinator]];
return self;
}
-(void)saveArticle:(Article *)article withURL:(NSString *)url onQueue:(dispatch_queue_t)queue
{
//save persistently in the background
dispatch_async(queue, ^{
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]) //ERROR OCCURS HERE, after many saves
{
//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];
}
});
}
/* Save notification handler for the background context */
- (void)backgroundContextDidSave:(NSNotification *)notification {
/* Make sure we're on the main thread when updating the main context */
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:@selector(backgroundContextDidSave:)
withObject:notification
waitUntilDone:NO];
return;
}
/* merge in the changes to the main context */
[self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
}
@end
好的,因此閱讀官方文檔會有所幫助。
從蘋果公司(重點是我):
並發
Core Data使用線程(或序列化隊列)限制來保護托管對象和托管對象上下文(請參閱“ Core Data的並發性”)。 這樣的結果是,上下文假定默認所有者是分配給它的線程或隊列,這是由調用其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];
}
});
}
是的,如果您使用限制並發模型(這是通過init獲得的),那么必須保證僅在創建MOC的線程中使用MOC。
您可以使用NSPrivateQueueConcurrencyType創建MOC,然后使用
[moc performBlock:^{
}];
執行操作。 它具有自己的內部隊列,並且將在后台運行所有請求,從而將訪問與其他調用同步。
您可以使用NSMainQueueConcurrencyType將MOC綁定為僅在主線程上運行。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.