【问题标题】:Restkit-loaded nested Core Data entities cause NSObjectInaccessibleExceptionRestkit 加载的嵌套核心数据实体导致 NSObjectInaccessibleException
【发布时间】:2012-02-08 22:20:46
【问题描述】:

我正在使用 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],我可以让所有对象恢复正常,并且它们可以毫无问题地加载。似乎只是在他们犯错的情况下。

【问题讨论】:

  • 希望您找到解决方案,我遇到了几乎相同的问题。
  • @ryan 如果你有任何发现请告诉我,到目前为止我还没有运气
  • @ryan - 看起来它可能不是 RestKit 特定的。后续问题:stackoverflow.com/questions/8856867/…
  • 我找到了适合我的解决方案。我试图在下面的回答中明确。希望对您有所帮助!

标签: objective-c ios core-data nsmanagedobject restkit


【解决方案1】:

我找到了一个对我有用的解决方案(我不确定它对您的情况有多适用,但我将其添加为答案,因为它为我解决了这个(或非常相似的)问题):

几天前,我运行了RKTwitterCoreData 示例,发现它运行良好,而我的,此时代码非常简单,做几乎相同的事情,却没有。我有很多很多未完成的错误。所以我决定修改我所有处理RestKit 的代码,以反映RKTwitterCoreData 示例是如何做到的。

我将把它分成几块来尝试帮助你遵循我当时的思路(因为我不认为我们的问题是相同的)。

我最初的实施假设

由于 RestKit 可以将对象返回给 Core Data,我假设这些托管对象可以互换使用。例如,我可以以与从远程 Web 服务检索的对象完全相同的方式使用 Core Data 中的对象。我什至可以将它们合并在一起以获取所有数据。

我错了

我注意到RKTwitterCoreData 的代码确实没有至少以这种方式流动。我的代码中有相当一部分与他们的代码相匹配,但最大的区别是他们没有将这些对象视为可互换的。事实上,他们从未使用从远程数据存储中获取的对象。相反,他们只是让它“从裂缝中消失”。我只能假设这意味着它们已添加到 Core Data 的数据存储中,因为它适用于他们,现在也适用于我。

详情

我的应用程序在修改我的代码以利用此流程后工作。我只能推测,我们看到的无法解决的错误与使用我们从 Web 服务返回的 Core Data 支持的对象有关。。相反,如果您只是忽略这些然后执行 fetch,您将取回所有内容(包括最近的请求)并且您不应该遇到任何无法完成的错误。

详细地说,如果您查看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;dr

不要使用您在 objectLoader:didLoadObjects: 中返回的托管对象,就好像它们完全由 Core Data 支持一样。相反,忽略它们并从 Core Data 重新获取。所有对象,包括来自最后一个请求的对象都在那里。否则,你会遇到无法弥补的错误(至少我是这样)。

【讨论】:

  • 我在调试过程中得出了同样的结论。他们似乎假设你会让它同步到 CD 然后重新查询。我遇到的问题是我不打算让我的所有对象都以 CD 为后盾(不想存储它们以备后用 - 只是这个视图渲染)。这迫使我使用返回的对象。我最终通过在 viewController 中保留 NSManagedObject 的 managedContext 来修复(阅读:以一种丑陋的方式进行黑客攻击)。这样做可以让我很好地访问 cellForIndexPath 中的对象。
  • 可能值得您花时间补充一下,作为对 RestKit 这一方面的任何其他不幸旅行者的回答。这个库给我留下了深刻的印象,但我觉得奇怪的是这个警告没有得到更好的记录。
  • 同意文档。我真的可以使用一些最新的文档而不是旧文档。感谢您在这里的洞察力。
【解决方案2】:

按照 Ryan 的建议记录我的修复(阅读:hack)。

错误似乎在于 RestKit 假设您将使用从其 objectLoader:didLoadObjects: 方法返回的对象。他们似乎认为它都将由 Core Data 支持(并遵循类似于 Ryan 所说的流程 - 让它同步到 Core Data,然后重新查询),或者您将使用所有非 Core Data 支持的对象并且只是保留这些结果。

在我的情况下,我有一个混合 - 一个非核心数据支持的对象的根数组,每个对象都包含一个核心数据支持的实体数组。顶级对象是我不介意查询服务器的对象,并且没有理由在显示它们的视图之外在本地持久化。似乎一旦objectLoader:didLoadObjects: 完成了支持核心数据实体的托管对象上下文objects 参数已被处理(假设您将重新查询它们),导致将来对实体的任何调用都被视为错误,即使您无法触发错误并加载数据(结果为NSObjectInaccessibleException)。

我用一个丑陋的 hack 解决了这个问题 - 在objectLoader:didLoadObjects: 中,我访问了一个核心数据实体的托管对象上下文并将其复制到视图中的一个属性 (self.context = [tag managedObjectContext];)。这可以防止它在objectLoader:didLoadObjects: 完成后释放上下文,允许我稍后在视图中访问实体而不会出现问题。

另一种解决方案是使用新上下文手动重新查询每个实体,并将其复制回存储的返回对象。当人们去显示它们时,或者可能在objectLoader:didLoadObjects: 中使用新的上下文进行一些后处理时,可以这样做。实体 ID 仍然存在于故障对象上,因此即使在原始 RestKit 上下文消失后,也可以使用它来重新查询而不会出现问题。但是,必须像这样重新查询对象图中的每个实体似乎很愚蠢。

【讨论】:

  • 您可以关闭自动同步,并在需要时手动将对象存储在 CD 模型中。但我需要一些关于此的文档!无论如何谢谢:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-26
  • 1970-01-01
  • 1970-01-01
  • 2011-12-04
  • 2014-04-11
  • 2014-06-16
相关资源
最近更新 更多