2012-08-08 68 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以僅在主線程上運行。

相關問題