CoreData+RESTKit:来自 NSFetchedResultsController 的同一对象的多个副本

Posted

技术标签:

【中文标题】CoreData+RESTKit:来自 NSFetchedResultsController 的同一对象的多个副本【英文标题】:CoreData+RESTKit: Multiple copies of same object from NSFetchedResultsController 【发布时间】:2014-02-15 13:46:16 【问题描述】:

我正在使用相同的 NSFetchedResultsController 通过 RESTKit 从 sqllite 支持的核心数据中获取一些结果,这些数据在多个视图控制器之间共享:

NSFetchedResultsController* _fetchedResultsController;

NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"User"];
fetchRequest.sortDescriptors = @[[[NSSortDescriptor alloc] initWithKey:@"username" ascending:YES]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"current == YES"];
[fetchRequest setPredicate:predicate];
_fetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
                                    managedObjectContext:[self __getContext]
                                      sectionNameKeyPath:nil cacheName:@"currentUser"];
_fetchedResultsController.delegate = self;
[_fetchedResultsController performFetch:error];
NSArray *array = [_fetchedResultsController fetchedObjects];
return array;

这在应用程序执行期间的多个阶段被调用,但是尽管我指定了缓存名称,但我得到了同一对象的多个副本,即不是单一的事实来源。

此问题仅在重新加载应用时发生。对象的初始映射没有问题。

我错过了什么吗?提前致谢

编辑:

首先是一些上下文:

我用当前属性标记当前登录的用户,任何需要访问当前用户的视图控制器都会执行缓存的获取请求。

在应用程序早期的某个时间点,一个视图控制器将自己作为观察者添加到从该获取请求中接收到的当前用户。在应用程序执行的另一点,用户创建一个相册,此时再次执行获取请求。这次对象的内存地址不一样,第一个视图控制器在将相册添加到用户集属性时没有收到KVO通知。

这里有一些额外的日志显示 RKInMemoryManagedObjectCache 开始返回不同的用户对象:

2014-02-15 16:15:53.196 [22074:a0b] user init: 0xf090b60 <x-coredata://56C4885F-954C-4843-9A59-52133777ECC5/User/p2><robbie> in context <NSManagedObjectContext: 0xf079790>
2014-02-15 16:15:53.198 [22074:a0b] [<RKInMemoryManagedObjectCache: 0xf0593b0>] In memory object cache is returning 0xf090b60 <x-coredata://56C4885F-954C-4843-9A59-52133777ECC5/User/p2><robbie> as current user
2014-02-15 16:15:53.209 [22074:a0b] [<RKInMemoryManagedObjectCache: 0xf0593b0>] In memory object cache is returning 0xf090b60 <x-coredata://56C4885F-954C-4843-9A59-52133777ECC5/User/p2><robbie> as current user
2014-02-15 16:15:53.209 [22074:a0b] [AlbumCollectionViewController] observing user 0xf090b60 <x-coredata://56C4885F-954C-4843-9A59-52133777ECC5/User/p2>: robbie
2014-02-15 16:15:53.209 [22074:a0b] [<RKInMemoryManagedObjectCache: 0xf0593b0>] In memory object cache is returning 0xf090b60 <x-coredata://56C4885F-954C-4843-9A59-52133777ECC5/User/p2><robbie> as current user
2014-02-15 16:15:53.262 [22074:a0b] [<RKInMemoryManagedObjectCache: 0xf0593b0>] In memory object cache is returning 0xf090b60 <x-coredata://56C4885F-954C-4843-9A59-52133777ECC5/User/p2><robbie> as current user
2014-02-15 16:15:53.262 [22074:a0b] [<RKInMemoryManagedObjectCache: 0xf0593b0>] In memory object cache is returning 0xf090b60 <x-coredata://56C4885F-954C-4843-9A59-52133777ECC5/User/p2><robbie> as current user
2014-02-15 16:15:53.269 [22074:a0b] [<RKInMemoryManagedObjectCache: 0xf0593b0>] In memory object cache is returning 0xf090b60 <x-coredata://56C4885F-954C-4843-9A59-52133777ECC5/User/p2><robbie> as current user
2014-02-15 16:15:53.269 [22074:a0b] [<RKInMemoryManagedObjectCache: 0xf0593b0>] In memory object cache is returning 0xf090b60 <x-coredata://56C4885F-954C-4843-9A59-52133777ECC5/User/p2><robbie> as current user
2014-02-15 16:15:53.278 [22074:a0b] applicationDidBecomeActive
2014-02-15 16:15:53.329 [22074:a0b] user init: 0xf342590 <x-coredata://56C4885F-954C-4843-9A59-52133777ECC5/User/p2><robbie> in context <NSManagedObjectContext: 0xf079790>
2014-02-15 16:15:53.329 [22074:a0b] [<RKInMemoryManagedObjectCache: 0xf0593b0>] In memory object cache is returning 0xf342590 <x-coredata://56C4885F-954C-4843-9A59-52133777ECC5/User/p2><robbie> as current user
2014-02-15 16:15:53.329 [22074:a0b] [<RKInMemoryManagedObjectCache: 0xf0593b0>] In memory object cache is returning 0xf342590 <x-coredata://56C4885F-954C-4843-9A59-52133777ECC5/User/p2><robbie> as current user
2014-02-15 16:15:53.346 [22074:a0b] [<RKInMemoryManagedObjectCache: 0xf0593b0>] In memory object cache is returning 0xf342590 <x-coredata://56C4885F-954C-4843-9A59-52133777ECC5/User/p2><robbie> as current user
2014-02-15 16:15:53.348 [22074:a0b] [<RKInMemoryManagedObjectCache: 0xf0593b0>] In memory object cache is returning 0xf342590 <x-coredata://56C4885F-954C-4843-9A59-52133777ECC5/User/p2><robbie> as current user

奇怪的是,一旦调用 applicationDidBecomeActive,它似乎总是开始返回不同的东西。

编辑2:

导致第一个用户初始化的调用堆栈:

#0  0x000e87f0 in -[User initWithEntity:insertIntoManagedObjectContext:] at /Users/mtford/Playground/App/App/App/CoreData/Human/User.m:107
#1  0x00f68a89 in -[NSManagedObject(_NSInternalMethods) _initWithEntity:withID:withHandler:withContext:] ()
#2  0x00f66ff2 in -[NSManagedObjectContext(_NSInternalAdditions) _retainedObjectWithID:optionalHandler:withInlineStorage:] ()
#3  0x00fb69b4 in _PFRetainedObjectIDCore ()
#4  0x00fb6880 in -[NSManagedObjectContext(_NSInternalAdditions) _retainedObjectWithID:] ()
#5  0x00fa7724 in -[NSManagedObjectContext(_NestedContextSupport) _parentObjectsForFetchRequest:inContext:error:] ()
#6  0x010251f4 in __82-[NSManagedObjectContext(_NestedContextSupport) executeRequest:withContext:error:]_block_invoke ()
#7  0x00fa7321 in internalBlockToNSManagedObjectContextPerform ()
#8  0x035fb4b0 in _dispatch_client_callout ()
#9  0x035e8778 in _dispatch_barrier_sync_f_invoke ()
#10 0x035e8422 in dispatch_barrier_sync_f ()
#11 0x00fa72a2 in _perform ()
#12 0x00fa714e in -[NSManagedObjectContext(_NestedContextSupport) executeRequest:withContext:error:] ()
#13 0x00f526c6 in -[NSManagedObjectContext executeFetchRequest:error:] ()
#14 0x00276367 in __96-[RKInMemoryManagedObjectCache managedObjectsWithEntity:attributeValues:inManagedObjectContext:]_block_invoke_2 at /Users/mtford/Playground/App/App/Pods/RestKit/Code/CoreData/RKInMemoryManagedObjectCache.m:112
#15 0x00fa6fef in developerSubmittedBlockToNSManagedObjectContextPerform ()
#16 0x00fa6f2c in -[NSManagedObjectContext performBlockAndWait:] ()
#17 0x00275ce4 in -[RKInMemoryManagedObjectCache managedObjectsWithEntity:attributeValues:inManagedObjectContext:] at /Users/mtford/Playground/App/App/Pods/RestKit/Code/CoreData/RKInMemoryManagedObjectCache.m:109
#18 0x000d73d9 in -[RestKitInterface getCurrentUsersWithError:] at /Users/mtford/Playground/App/App/App/RESTKit/Old/RestKitInterface.m:647
#19 0x000e9613 in +[User currentUser] at /Users/mtford/Playground/App/App/App/CoreData/Human/User.m:221
#20 0x0002f6f0 in -[TabBarController viewWillAppear:] at /Users/mtford/Playground/App/App/App/View Controllers/AppTabBarController.m:100

调用堆栈导致第二个用户初始化:

#0  0x000e87f0 in -[User initWithEntity:insertIntoManagedObjectContext:] at /Users/mtford/Playground/App/App/App/CoreData/Human/User.m:107
#1  0x00f68a89 in -[NSManagedObject(_NSInternalMethods) _initWithEntity:withID:withHandler:withContext:] ()
#2  0x00f66ff2 in -[NSManagedObjectContext(_NSInternalAdditions) _retainedObjectWithID:optionalHandler:withInlineStorage:] ()
#3  0x00fb69b4 in _PFRetainedObjectIDCore ()
#4  0x00f7ffbf in -[NSManagedObjectContext(_NSInternalAdditions) _retainedObjectWithID:error:] ()
#5  0x00f7fef5 in -[NSManagedObjectContext existingObjectWithID:error:] ()
#6  0x0025e29a in __56-[RKEntityByAttributeCache objectForObjectID:inContext:]_block_invoke at /Users/mtford/Playground/App/App/Pods/RestKit/Code/CoreData/RKEntityByAttributeCache.m:222
#7  0x00fa6fef in developerSubmittedBlockToNSManagedObjectContextPerform ()
#8  0x00fa6f2c in -[NSManagedObjectContext performBlockAndWait:] ()
#9  0x0025dd7c in -[RKEntityByAttributeCache objectForObjectID:inContext:] at /Users/mtford/Playground/App/App/Pods/RestKit/Code/CoreData/RKEntityByAttributeCache.m:221
#10 0x0025ea74 in -[RKEntityByAttributeCache objectsWithAttributeValues:inContext:] at /Users/mtford/Playground/App/App/Pods/RestKit/Code/CoreData/RKEntityByAttributeCache.m:255
#11 0x00265644 in -[RKEntityCache objectsForEntity:withAttributeValues:inContext:] at /Users/mtford/Playground/App/App/Pods/RestKit/Code/CoreData/RKEntityCache.m:90
#12 0x00275f15 in -[RKInMemoryManagedObjectCache managedObjectsWithEntity:attributeValues:inManagedObjectContext:] at /Users/mtford/Playground/App/App/Pods/RestKit/Code/CoreData/RKInMemoryManagedObjectCache.m:132
#13 0x000d73d9 in -[RestKitInterface getCurrentUsersWithError:] at /Users/mtford/Playground/App/App/App/RESTKit/Old/RestKitInterface.m:647
#14 0x000e9613 in +[User currentUser] at /Users/mtford/Playground/App/App/App/CoreData/Human/User.m:221
#15 0x00003efb in -[AlbumCollectionViewController observeValueForKeyPath:ofObject:change:context:] at /Users/mtford/Playground/App/App/App/View Controllers/AlbumCollectionViewController.m:179

【问题讨论】:

您能否展示重复的示例,您如何检查/比较它们? 嗨 Wain,已添加到问题中。 我做了更多调查,似乎内存中的对象缓存在调用 applicationDidBecomeActive 后创建了一个全新的对象。不知道是不是巧合…… 在您的场景中使用 RestKit 提出的任何请求? RestKit 中配置的标识属性?这些日志来自哪里(什么创建了新用户)? RestKit 不发出任何请求,一切都纯粹从 sqlite 加载。我已经在问题中放置了导致[User init] 的调用堆栈。您可以看到 RKInMemoryManagedObjectCache 涉及两者,而我相信它应该只创建一个实例。确实配置了标识属性。从现在开始,对象缓存使用标识属性正确映射到第二个创建的用户。 【参考方案1】:

在我们使用 RESTKit 和 CoreData 的项目中,我们也遇到了相同的重复问题,避免重复的最简单方法是使用 setIdentificationAttributes 将唯一 id 参数设置为实体映射

【讨论】:

检查cmets,OP有标识属性,问题场景中没有映射...

以上是关于CoreData+RESTKit:来自 NSFetchedResultsController 的同一对象的多个副本的主要内容,如果未能解决你的问题,请参考以下文章

RestKit 强制 CoreData 保存

Restkit 的核心数据 [重复]

不使用 Web 服务时如何使用来自 RestKit 的核心数据?

服务器端更改的 Restkit.Delete

RestKit 与直接 CoreData

RestKit 和 Coredata