子上下文更改传播到其他子上下文(相同层次结构级别)而不合并
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
获取上下文中的对象。
【讨论】:
以上是关于子上下文更改传播到其他子上下文(相同层次结构级别)而不合并的主要内容,如果未能解决你的问题,请参考以下文章
在 Core Data 中执行子获取时,在父上下文中修改托管对象是不是会向下传播到子上下文?