我正在使用RestKit从我的RoR服务中获取对象,并使用CoreData来保留一些对象(更多的静态类型查找表对象)。 TasteTag是其中一个持久对象:
#ifdef RESTKIT_GENERATE_SEED_DB
NSString *seedDatabaseName = nil;
NSString *databaseName = RKDefaultSeedDatabaseFileName;
#else
NSString *seedDatabaseName = RKDefaultSeedDatabaseFileName;
NSString *databaseName = @"Model.sqlite";
#endif
RKObjectManager* manager = [RKObjectManager objectManagerWithBaseURL:kServerURL];
manager.objectStore = [RKManagedObjectStore objectStoreWithStoreFilename:databaseName usingSeedDatabaseName:seedDatabaseName managedObjectModel:nil delegate:self];
.. lots of fun object mapping ..
RKManagedObjectMapping* tasteTagMapping = [RKManagedObjectMapping mappingForClass:[TasteTag class]];
[tasteTagMapping mapKeyPath:@"id" toAttribute:@"tasteTagID"];
[tasteTagMapping mapKeyPath:@"name" toAttribute:@"name"];
tasteTagMapping.primaryKeyAttribute = @"tasteTagID";
[[RKObjectManager sharedManager].mappingProvider setMapping:tasteTagMapping forKeyPath:@"taste_tags"];
[[RKObjectManager sharedManager].mappingProvider addObjectMapping:tasteTagMapping];
.. some more mapping ..
我从RoR服务器返回数据,并按预期将其映射到对象。在RestKit收到请求后,Core Data实体似乎也正常映射:
"<TasteTag: 0x6e87170> (entity: TasteTag; id: 0x6e85d60 <x-coredata://03E4A20A-21F2-4A2D-92B4-C4424893D559/TasteTag/p5> ; data: <fault>)"
问题是当我尝试访问对象的属性时,故障似乎无法解决。起初我只是调用属性,这些属性总是以零形式返回(即使这应该触发错误):
for (TasteTag *tag in self.vintage.tasteTags) {
[tagNames addObject:tag.name]; //get error of trying to add nil to array
}
在查看手动触发错误(http://www.mlsite.net/blog/?p=518)后,我尝试调用[tag willAccessValueForKey:nil]
,结果是:
Terminating app due to uncaught exception 'NSObjectInaccessibleException', reason: 'CoreData could not fulfill a fault for '0x6e7b060 <x-coredata://03E4A20A-21F2-4A2D-92B4-C4424893D559/TasteTag/p5>''
根据键(TasteTag / p5)查找.sqlite中的实体确实显示它映射到我期望的那个。
与RestKit相关的其他帖子建议禁用对象缓存(我没有使用),因为这通常是由被删除的实体引起的。但是在这个阶段我只是阅读而不是删除,而且我没有缓存。
如果我只是打电话给[TasteTag allObjects]
我能够让所有对象恢复正常并加载它们没有问题。它只是出现故障的情况。
答案 0 :(得分:10)
我找到了一个对我有用的解决方案(我不确定它对您的情况有多适用,但我将其作为答案添加,因为它为我解决了这个(或非常类似的)问题):
几天前,我运行了RKTwitterCoreData
示例并发现它在我的时候工作得很好,此时代码非常简单并做了几乎相同的事情,但事实并非如此。我收到了很多未完成的错误很多。因此,我决定修改处理RestKit
的所有代码,以反映RKTwitterCoreData
示例的用法。
我会把它分成几块,试着帮助你按照我当时的思路(因为我不认为我们的问题是相同的)。
我的原始实施假设
由于RestKit可以将对象备份到Core Data,因此我假设这些托管对象可以互换使用。例如,我可以使用Core Data中的对象,其方式与从远程Web服务检索的对象完全相同。我甚至可以将它们合并在一起以获取所有数据。
我错了
我注意到RKTwitterCoreData
的代码不至少以这种方式流动。我的代码中有一大块与他们的代码匹配,但最大的区别在于他们没有将这些对象视为可互换的。事实上,他们从未使用从远程数据存储中获取的对象。相反,他们只是让它“陷入困境”。我只能假设这意味着它们被添加到Core Data的数据存储中,因为它适用于它们,现在,对我而言。
<强>详情
我的应用程序在修改我的代码以使用此流程后工作。我只能猜测,我们看到的无法弥补的错误与使用我们从Web服务返回的Core Data支持的对象有关。如果您只是忽略它们然后进行提取,您将获得所有内容(包括最新的请求),并且您不应该得到任何无法弥补的错误。
详细说明,如果你看一下RKTwitterViewController
,你会注意到第45-61行处理对象的加载:
- (void)loadObjectsFromDataStore {
[_statuses release];
NSFetchRequest* request = [RKTStatus fetchRequest];
NSSortDescriptor* descriptor = [NSSortDescriptor sortDescriptorWithKey:@"createdAt" ascending:NO];
[request setSortDescriptors:[NSArray arrayWithObject:descriptor]];
_statuses = [[RKTStatus objectsWithFetchRequest:request] retain];
}
- (void)loadData {
// Load the object model via RestKit
RKObjectManager* objectManager = [RKObjectManager sharedManager];
[objectManager loadObjectsAtResourcePath:@"/status/user_timeline/RestKit" delegate:self block:^(RKObjectLoader* loader) {
// Twitter returns statuses as a naked array in JSON, so we instruct the loader
// to user the appropriate object mapping
loader.objectMapping = [objectManager.mappingProvider objectMappingForClass:[RKTStatus class]];
}];
}
一切看起来都很正常(至少与我最初的加载方式相比)。但是看看objectLoader:didLoadObjects:
委托方法:
- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjects:(NSArray*)objects {
[[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:@"LastUpdatedAt"];
[[NSUserDefaults standardUserDefaults] synchronize];
NSLog(@"Loaded statuses: %@", objects);
[self loadObjectsFromDataStore];
[_tableView reloadData];
}
样本甚至没有触及objects
参数! (除了NSLog
当然......)
<强>结论/ TL;博士强>
请勿使用您在objectLoader:didLoadObjects:
中获得的托管对象,就好像它们完全由Core Data支持一样。相反,忽略它们并从Core Data重新获取。所有对象,包括上次请求中的对象都在那里。否则,你将得到无法弥补的错误(至少我做过)。
答案 1 :(得分:4)
根据Ryan的建议记录我的修复(读取:hack)。
错误似乎在于RestKit假设您将使用从objectLoader:didLoadObjects:
方法返回的对象。他们似乎认为它将全部支持Core Data(并遵循类似于Ryan所说的流程 - 让它同步到Core Data,然后重新查询)或者您将使用所有非Core Data支持的对象并且只是保持这些结果。
在我的情况下,我有一个混合 - 非核心数据支持对象的根数组,每个对象都包含一个Core Data支持的实体数组。顶级对象是我不介意查询服务器的对象,并且没有理由在其显示的视图之外本地持久化。似乎objectLoader:didLoadObjects:
完成支持核心数据的托管对象上下文objects
param中的实体被丢弃(假设您将重新查询它们),导致将来对实体的任何调用导致被视为错误,即使您无法触发故障并加载数据(结果为NSObjectInaccessibleException
)。
我用一个丑陋的黑客来解决它 - 在objectLoader:didLoadObjects:
内我访问Core Data实体的一个托管对象上下文并将其复制到视图中的属性(self.context = [tag managedObjectContext];
)。这可以防止在objectLoader:didLoadObjects:
完成后释放上下文,从而允许我在视图中稍后访问实体而不会出现问题。
另一种解决方案是使用新上下文手动重新查询每个实体,并将其复制回存储的返回对象。当人们使用新的上下文显示它们,或者可能在objectLoader:didLoadObjects:
中进行一些后处理时,可以做到这一点。实体ID仍在故障对象上,因此即使在原始RestKit上下文消失后,也可以使用它来重新查询而不会出现问题。但是,必须像对象那样重新查询对象图中的每个实体似乎很愚蠢。