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

时间:2014-08-01 12:49:12

标签: core-data merge parent-child nsmanagedobjectcontext

我正在实现CoreData堆栈 https://stackoverflow.com/a/24663533(图片中的选项A)但它以意想不到的方式工作。

我有 rootContext (NSPrivateQueueConcurrencyType),它有两个子节点: uiContext (NSMainQueueConcurrencyType)用于提取对象, syncContext (NSPrivateQueueConcurrencyType)用于异步数据编辑。

正如我想的那样,当我在performBlock(后台队列)中的syncContext中保存内容时,更改将传播到rootContext,但是直到我观察NSManagedObjectContextDidSaveNotification并合并通知中的更改时才会更改uiContext。但是在syncContext保存后立即反映出更改。

我的第一个问题是:为什么没有人工合并更新uiContext?

我的第二个问题:为什么在syncContext保存后在后台(而不是主线程)上修改了rootContext?前段时间我问过关于" CoreData无法解决问题的问题" MagicalRecord 'CoreData could not fulfill a fault' error with MagicalRecord的问题,但我没有得到答案,所以我决定找到没有外部库的解决方案。

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

这是我的源代码:

#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

1 个答案:

答案 0 :(得分:0)

它们未合并到UI上下文中。您是手动提取它们。

当您在syncContext中保存时,数据会被推送到rootContext中。数据未合并到uiContext中。但是,当您执行提取时,提取会从父上下文中提取数据。

您可以在registeredObjects

的上下文中获取对象