Restkit [NSManagedObject ManagedObjectContext] 返回不同的实例

发布于 2024-12-27 22:47:27 字数 1944 浏览 1 评论 0原文

自从我将核心数据逻辑迁移到 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 的 ma​​nagedObjectContext 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

白龙吟 2025-01-03 22:47:27

经过几个小时的调试,我终于找到了那个令人讨厌的错误。问题在于 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 to RKManagedObjectStore. 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 the RKManagedObjectStore in your App Delegate and you're done.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文