2012-08-08 124 views
1

我已经阅读了关于此错误的所有内容,但仍无法确定它在我的应用程序中发生的原因。当在后台保存核心数据时出现NSInternalInconsistencyException异常

我使用后台保存上下文几个核心数据对象时出现以下错误:

*** 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.

在下面的代码,ArticleManageraddArticle是所谓的主线程循环。可能会添加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 

回答

1

OK,所以阅读官方文档是有点用的。

从苹果(重点煤矿):

并发

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

所以我的问题是,我被初始化主线程的背景下,但随后通过dispatch_async执行在后台线程保存(使用是在主线程创建的上下文中)使用大中央调度。

我通过添加上下文初始化到后台块固定它:

-(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]; 
     } 
    }); 
} 
0

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

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

[moc performBlock:^{ 
}]; 

执行操作。它有自己的内部队列,并且会在后台运行所有请求,将访问与其他呼叫同步。

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