簡體   English   中英

在后台上下文中保存核心數據時出現NSInternalInconsistencyException異常

[英]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.

在下面的代碼, 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

好的,因此閱讀官方文檔會有所幫助。

從蘋果公司(重點是我):

並發

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM