このエラーに関するSOのすべてを読みましたが、アプリでなぜそれが起こっているのかを突き止めることはできません.
バックグラウンド コンテキストを使用して複数の 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
'sが呼び出されます。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