#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>''
在.sqlite中查找基于键的实体(TasteTag / p5)确实显示它映射到我期望的那个.
与RestKit相关的其他帖子建议禁用对象缓存(我没有使用),因为这通常是由实体被删除引起的.但是在这个阶段我只是阅读而不是删除,而且我没有缓存.
如果我只是调用[TasteTag allObjects],我可以将所有对象恢复正常,并且它们可以毫无问题地加载.它只是出现故障的情况.
解决方法
几天前,我运行了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:delegate方法:
- (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]; }
样本甚至没有触摸对象参数! (当然除了NSLog ……)
结论/ TL;博士
不要使用在objectLoader中返回的托管对象:didLoadObjects:就像它们完全由Core Data支持一样.相反,忽略它们并从Core Data重新获取.所有对象,包括上次请求中的对象都在那里.否则,你将得到无法弥补的错误(至少我做过).