Restkit [NSManagedObject ManagedObjectContext] 返回不同的实例
自从我将核心数据逻辑迁移到 RKManagedObjectStore 以来,我遇到了严重的问题。我在视图控制器的主线程中设置了一个 NSFetchedResultsController,并将上下文设置为 [NSManagedObject ManagedObjectContext]:
assert([NSThread isMainThread]);
NSManagedObjectContext* context = [NSManagedObject managedObjectContext];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:[Item fetchRequest] managedObjectContext:context sectionNameKeyPath:nil cacheName:@"Master"];
我在上下文中插入对象,如下所示:
Item* item = [Item object];
item.name = @"Foo";
[[RKObjectManager sharedManager].objectStore save];
但获取的结果控制器不会收到更改通知。因此,我手动注册了一个通知:
[[NSNotificationCenter defaultCenter] addObserverForName:NSManagedObjectContextDidSaveNotification object:nil queue:nil usingBlock:^(NSNotification *note) {
NSLog(@"Context changed");
[self.fetchedResultsController performFetch:nil];
[self.tableView reloadData];
}];
我认为这实际上没有必要,因为 RKManagedObjectStore 合并了不同上下文之间的更改。其次,为了删除 Item 对象,我尝试了
[item deleteEntity];
这产生了一个错误,指出无法在另一个上下文中删除该对象。这显然是正确的,但是为什么上下文不是主线程的同一个实例?在删除实体之前,我还在视图控制器内调用以下内容:
assert([NSThread isMainThread]);
NSManagedObjectContext* sameContext1 = [NSManagedObject managedObjectContext];
NSManagedObjectContext* sameContext2 = self.fetchedResultsController.managedObjectContext;
assert(sameContext1 == sameContext2); //FAILS
查看使用 [NSManagedObject ManagedObjectContext] 时调用的 RKManagedObjectStore 的 managedObjectContext getter 实现,每个线程应返回相同的实例:
-(NSManagedObjectContext*)managedObjectContext {
NSMutableDictionary* threadDictionary = [[NSThread currentThread] threadDictionary];
NSManagedObjectContext* backgroundThreadContext = [threadDictionary objectForKey:RKManagedObjectStoreThreadDictionaryContextKey];
...
}
I have serious problems since I migrated my core data logic to RKManagedObjectStore. I setup a NSFetchedResultsController with the context set to [NSManagedObject managedObjectContext] within the main thread in the view controller:
assert([NSThread isMainThread]);
NSManagedObjectContext* context = [NSManagedObject managedObjectContext];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:[Item fetchRequest] managedObjectContext:context sectionNameKeyPath:nil cacheName:@"Master"];
I insert objects in the context like this:
Item* item = [Item object];
item.name = @"Foo";
[[RKObjectManager sharedManager].objectStore save];
But the fetched results controller doesn't get notified of the changes. Thus I registered a notification manually:
[[NSNotificationCenter defaultCenter] addObserverForName:NSManagedObjectContextDidSaveNotification object:nil queue:nil usingBlock:^(NSNotification *note) {
NSLog(@"Context changed");
[self.fetchedResultsController performFetch:nil];
[self.tableView reloadData];
}];
This should really not be necessary I think, since RKManagedObjectStore merges changes across different contexts. Second, for deleting the Item object I tried
[item deleteEntity];
This produced an error saying that the object cannot be deleted within another context. This is obviously true, but WHY the hell is the context not the same instance for the Main Thread? I call the following also inside the view controller just before deleting en entity:
assert([NSThread isMainThread]);
NSManagedObjectContext* sameContext1 = [NSManagedObject managedObjectContext];
NSManagedObjectContext* sameContext2 = self.fetchedResultsController.managedObjectContext;
assert(sameContext1 == sameContext2); //FAILS
Looking at RKManagedObjectStore's managedObjectContext getter Implementation which is called when using [NSManagedObject managedObjectContext], the same instance per thread should be returned:
-(NSManagedObjectContext*)managedObjectContext {
NSMutableDictionary* threadDictionary = [[NSThread currentThread] threadDictionary];
NSManagedObjectContext* backgroundThreadContext = [threadDictionary objectForKey:RKManagedObjectStoreThreadDictionaryContextKey];
...
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
经过几个小时的调试,我终于找到了那个令人讨厌的错误。问题在于
RKObjectManager
持有对RKManagedObjectStore
的引用。但不知何故,当使用 ARC 时,该引用不会保留在[RKObjectManager shareManager]
实例中,并且会被释放。这会导致线程本地缓存的刷新。因此,托管对象上下文合并不起作用,因为每次访问都会创建一个新的托管上下文。修复很容易。只需在您的 App Delegate 中保留对RKManagedObjectStore
的强引用即可。I finally tracked it down that nasty bug after hours of debugging. The problem is that
RKObjectManager
holds a reference toRKManagedObjectStore
. But somehow when using ARC that reference is not kept in the[RKObjectManager sharedManager]
instance and is being deallocated. That causes flushing of the thread local cache. Therefore managed object context merging is not working because on every access a new managed context is created. The fix is easy. Just keep a strong reference to theRKManagedObjectStore
in your App Delegate and you're done.