托管对象上下文不合并来自后台上下文的更改

Posted

技术标签:

【中文标题】托管对象上下文不合并来自后台上下文的更改【英文标题】:Managed object context not merging changes from background context 【发布时间】:2013-05-10 12:56:31 【问题描述】:

我有一个双重托管对象上下文设置,其中我有一个父/子 MOC 关系。父级负责直接写入私有队列(NSPrivateQueueConcurrencyType)上的数据库,然后我有一个子上下文负责为主线程(NSMainQueueConcurrencyType)拉取和保存数据。

当我对主队列上下文进行更改并保存它时,上下文会正常工作,然后将更改合并到后台队列上下文并在后台线程上写入数据库。

我遇到的问题是当我将数据直接写入后台队列上下文,然后尝试将其合并到主队列上下文时。对象正确存储在商店中,当我合并更改时,它似乎可以工作。但是,如果我尝试在将数据保存到存储后直接针对主队列上下文发出 NSFetchRequest,那么数据是陈旧的,而不是正确的更新数据。

以下是代码摘录,可以帮助大家了解我在做什么。需要注意的是从 JSON 返回 NSManagedObjects 的逻辑可以正常工作。此外,我已经测试并且在 UI 尝试对主队列 MOC 发出任何获取请求之前运行 mergeChangesFromContextDidSaveNotification。大家有什么想法吗?

// Context Setup
- (NSManagedObjectContext *)backgroundWriterManagedObjectContext

    if (_backgroundWriterManagedObjectContext != nil)
        return _backgroundWriterManagedObjectContext;

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil)
    
        _backgroundWriterManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
        _backgroundWriterManagedObjectContext.persistentStoreCoordinator = coordinator;
        _backgroundWriterManagedObjectContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy;
        _backgroundWriterManagedObjectContext.undoManager = nil;
    
    return _backgroundWriterManagedObjectContext;


- (NSManagedObjectContext *)managedObjectContext

    if (_managedObjectContext != nil)
        return _managedObjectContext;

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil)
    
        _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
        _managedObjectContext.parentContext = _backgroundWriterManagedObjectContext;
        _managedObjectContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy;
        _managedObjectContext.stalenessInterval = 0.0;
    
    return _managedObjectContext;


// save MOC
- (void)saveManagedObjectContext

    @try
    
        // perform synchronous process to save to the main MOC
        [_managedObjectContext performBlockAndWait:^(void)
        
            __block NSError *error = nil;

            // push any changes in the main context to the background writer context
            if (![_managedObjectContext tryLock])
                [VS_Log VS_LogError:[NSString stringWithFormat:@"VS_CoreDataManger - Unable To Lock Managed Object Context: %@", error.localizedDescription]];

            if (![_managedObjectContext save:&error])
                [VS_Log VS_LogError:[NSString stringWithFormat:@"VS_CoreDataManger - Error Saving Managed Object Context: %@", error.localizedDescription]];

            [_managedObjectContext unlock];

            // save the background writer context
            [_backgroundWriterManagedObjectContext performBlock:^(void)
            
                error = nil;

                if (![_backgroundWriterManagedObjectContext tryLock])
                    [VS_Log VS_LogError:[NSString stringWithFormat:@"VS_CoreDataManger - Unable To Lock Background Writer Managed Object Context: %@", error.localizedDescription]];

                if (![_backgroundWriterManagedObjectContext save:&error])
                    [VS_Log VS_LogError:[NSString stringWithFormat:@"VS_CoreDataManger - Error Saving Background Writer Managed Object Context: %@", error.localizedDescription]];

                [_backgroundWriterManagedObjectContext unlock];
            ];
        ];
    
    @catch (NSException *exception)
    
        [VS_Log VS_LogException:exception];
    


// This code runs when attempting to save objects from a web service call
[_backgroundWriterManagedObjectContext performBlock:^(void)
    
        // create a new process object and add it to the dictionary
        __block VS_CoreDataRequest *request = [[VS_CoreDataRequest alloc] init];

        // .. LOGIC HERE DESERIALIZES THE JSON AND RETURNS AN ARRAY OF NSMANAGEDOBJECTS

         // perform synchronous process to save to the main MOC
         @try
         
             NSError *error = nil;

             [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mergeChangesFromContextDidSaveNotification:) name:NSManagedObjectContextDidSaveNotification object:_backgroundWriterManagedObjectContext];

             if (![_backgroundWriterManagedObjectContext tryLock])
                 [VS_Log VS_LogError:[NSString stringWithFormat:@"VS_CoreDataManger - Unable To Lock Background Writer Managed Object Context: %@", error.localizedDescription]];

             if (![_backgroundWriterManagedObjectContext save:&error])
                 [VS_Log VS_LogError:[NSString stringWithFormat:@"VS_CoreDataManger - Error Saving Background Writer Managed Object Context: %@", error.localizedDescription]];

             [_backgroundWriterManagedObjectContext unlock];

             // submit changes back to the forground context
             [_managedObjectContext performBlock:^(void)
             
                 NSMutableArray *objects = [[NSMutableArray alloc] init];

                 // iterate through the updated objects and find them in the main thread MOC
                 for (VS_BaseManagedObject *object in request.objects)
                 
                     // get the object from the main managed object context
                     NSError *error;
                     NSManagedObject *obj = [_managedObjectContext existingObjectWithID:object.objectID error:&error];

                     if (error)
                         [VS_Log VS_LogError:[NSString stringWithFormat:@"VS_CoreDataManager - Error: %@", error.localizedDescription]];

                     if (obj)
                     
                         [_managedObjectContext refreshObject:obj mergeChanges:YES];
                         [objects addObject:obj];
                     
                 

                 // RETURN FROM METHOD SO UI CAN REGAIN CONTROL
             ];
         
         @catch (NSException *exception)
         
            [VS_Log VS_LogException:exception];
            return;
         
     ];

// Merges changes from parent to child context
- (void)mergeChangesFromContextDidSaveNotification:(NSNotification *)notification

    // remove observer
    [[NSNotificationCenter defaultCenter] removeObserver:self name:NSManagedObjectContextDidSaveNotification object:_backgroundWriterManagedObjectContext];

    // merge the changes
    [_managedObjectContext mergeChangesFromContextDidSaveNotification:notification];

【问题讨论】:

在您提供的信息中有些东西没有意义,因为您的代码和概念看起来不错。您是否使用 FRC 将数据提供给视图?如果是这样,我怀疑它与您的 FRC 有更多关系。我不确定独立提取是否会根据您的需要更新 MOC。我会调查的。 【参考方案1】:

您没有调用进程挂起的更改。

【讨论】:

以上是关于托管对象上下文不合并来自后台上下文的更改的主要内容,如果未能解决你的问题,请参考以下文章

CoreData 合并冲突显示托管对象版本更改而不是数据

临时托管对象未正确从子上下文合并到主上下文

在后台队列中保存临时托管对象上下文

在后台和主线程中使用托管对象上下文

Core Data 托管对象上下文线程同步

合并托管对象上下文的方法