子上下文更改传播到其他子上下文(相同层次结构级别)而不合并

Posted

技术标签:

【中文标题】子上下文更改传播到其他子上下文(相同层次结构级别)而不合并【英文标题】:Child context changes are propagated to other child context (same hierarchy level) without merge 【发布时间】:2014-08-01 12:49:12 【问题描述】:

我正在根据 https://***.com/a/24663533(图片中的选项 A)但它以一种意想不到的方式工作。

我有 rootContext (NSPrivateQueueConcurrencyType),它有 2 个孩子:uiContext (NSMainQueueConcurrencyType) 用于对象获取和 syncContext (NSPrivateQueueConcurrencyType) 用于异步数据编辑。

正如我所想,当我在 performBlock(后台队列)中的 syncContext 中保存某些内容时,更改将传播到 rootContext,但 uiContext 不会更改,直到我观察到 NSManagedObjectContextDidSaveNotification 并合并通知中的更改。但是同步上下文保存后会立即反映更改。

我的第一个问题是:为什么不用手动合并就更新了uiContext?

我的第二个问题:为什么在 syncContext 保存后 rootContext 会在后台(而不是主线程)修改?前段时间,我用 MagicalRecord 'CoreData could not fulfill a fault' error with MagicalRecord 询问了有关“CoreData 无法完成故障”的问题,但我没有收到答案,所以我决定在没有外部库的情况下寻找解决方案。

看来,主线程正在读取对象属性,并且在后台删除了同一个对象,而主线程上的操作员仍然没有返回控制权。

这是我的源代码:

#import <CoreData/CoreData.h>
#import "DataLayer.h"
#import "Person.h"

@interface DataLayer ()

@property (nonatomic, strong) NSManagedObjectModel *model;
@property (nonatomic, strong) NSPersistentStoreCoordinator *coordinator;

@property (nonatomic, strong) NSManagedObjectContext *rootContext;
@property (nonatomic, strong) NSManagedObjectContext *uiContext;
@property (nonatomic, strong) NSManagedObjectContext *syncContext;

@end

@implementation DataLayer

+ (void)load

    [self instance];


+ (DataLayer *)instance

    static DataLayer *instance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^
        instance = [[DataLayer alloc] init];
    );
    return instance;


- (instancetype)init

    self = [super init];
    if (self) 

        [self initModel];
        [self initCoordinator];
        [self initContexts];
        [self observeContextSaveNotification];

        [self startTesting];
    
    return self;


- (void)initModel

    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Model" withExtension:@"momd"];
    self.model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];


- (void)initCoordinator

    NSURL *directory = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
    NSURL *storeURL = [directory URLByAppendingPathComponent:@"Model.sqlite"];
    NSError *error = nil;
    self.coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.model];
    if (![self.coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) 
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    


- (void)initContexts

    self.rootContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    self.rootContext.persistentStoreCoordinator = self.coordinator;

    self.uiContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
    self.uiContext.parentContext = self.rootContext;

    self.syncContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    self.syncContext.parentContext = self.rootContext;


- (void)observeContextSaveNotification

//    [[NSNotificationCenter defaultCenter] addObserver:self
//                                             selector:@selector(onManagedObjectContextDidSaveNotification:)
//                                                 name:NSManagedObjectContextDidSaveNotification
//                                               object:nil];


- (void)onManagedObjectContextDidSaveNotification:(NSNotification *)notification

//    NSManagedObjectContext *context = notification.object;
//    if (context != self.uiContext) 
//        [self.uiContext mergeChangesFromContextDidSaveNotification:notification];
//    


- (void)startTesting

    NSArray *personsBeforeSave = [self fetchEntities:@"Person" fromContext:self.uiContext];
    NSLog(@"Before save: %i persons in syncContext", [personsBeforeSave count]); // Before save: 0 persons in syncContext

    [self.syncContext performBlock:^

        Person *person = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:self.syncContext];
        person.firstName = @"Alexander";

        NSError *error = nil;
        [self.syncContext save:&error];
        if (error) 
            NSLog(@"Error during save: %@", error);
        

        NSArray *personsAfterSaveFromBackground = [self fetchEntities:@"Person" fromContext:self.rootContext];
        NSLog(@"After save from background: %i persons in rootContext", [personsAfterSaveFromBackground count]); // After save from background: 1 persons  in rootContext

        dispatch_async(dispatch_get_main_queue(), ^
            NSArray *personsAfterSaveFromMain = [self fetchEntities:@"Person" fromContext:self.uiContext];
            NSLog(@"After save from main: %i persons in uiContext", [personsAfterSaveFromMain count]); // After save from main: 1 persons in uiContext
        );
    ];


- (NSArray *)fetchEntities:(NSString *)entity fromContext:(NSManagedObjectContext *)context

    NSError *error = nil;
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:entity];
    NSArray *result = [context executeFetchRequest:request error:&error];

    if (error) 
        NSLog(@"Error during fetch %@: %@", entity, error);
        return nil;
    

    return result;


@end

【问题讨论】:

我也面临同样的现象:***.com/questions/30016148/… 查看更改如何传播 benedictcohen.co.uk/blog/archives/308 【参考方案1】:

它们没有被合并到 UI 上下文中。您正在手动获取它们。

当您保存在同步上下文中时,数据会被推送到根上下文中。数据不会合并到 uiContext 中。但是,当您执行 fetch 时,fetch 会从父上下文中拉取数据。

您可以使用registeredObjects 获取上下文中的对象。

【讨论】:

以上是关于子上下文更改传播到其他子上下文(相同层次结构级别)而不合并的主要内容,如果未能解决你的问题,请参考以下文章

springcloud应用程序上下文层次结构

springcloud应用程序上下文层次结构

在 Core Data 中执行子获取时,在父上下文中修改托管对象是不是会向下传播到子上下文?

在根和子应用程序上下文中具有相同名称的 Bean?

Web 应用程序上下文层次结构中的 Spring bean 范围

核心数据拉动变化