【发布时间】:2012-10-19 04:54:46
【问题描述】:
我现在被一个令人讨厌的核心数据问题困住了大约两个星期。我阅读了很多博文、文章和 SO 问题/答案,但我仍然无法解决我的问题。
我进行了大量测试,能够将较大的问题缩减为较小的问题。 这将是一个很大的解释,所以请继续关注我!
问题 - 数据模型
我必须得到以下数据模型:
对象 A 与对象 B 具有一对多关系,而对象 B 与对象 C 具有另一个一对多关系。由于 Core Data 的建议,我必须创建反向关系,因此 B 的每个实例都指向其父 A 并且指向其父 B 的 C 也是如此。
A <->> B <->> C
问题 - MOC 设置
为了保持响应顺畅,我创建了一个三级 managedObjectContext 结构。
- 父 MOC - 使用
NSPrivateQueueConcurrencyType在自己的私有线程上运行,与persistentStoreCoordinator紧密相连 - MainQueue MOC - 使用
NSMainQueueConcurrencyType在 mainThread 上运行并拥有父 MOC 1 - 对于每个解析操作,我都会创建第三个 MOC,它也有它的私有队列和父 mainQueue MOC
我的主数据控制器作为观察者添加到 MOC 2 的 NSManagedObjectContextDidSave 通知中,因此每次 MOC 2 在 MOC1 上保存 performBlock: 时都会触发执行保存操作(由于 performBlock: 而异步)。
问题 - 解析
为了将大型 JSON 文件解析为我的核心数据结构,我编写了一个循环解析器。这个解析器首先创建一个新的 MOC (3)。然后它获取对象 A 的数据并解析其属性。然后解析器读取 B 的 JSON 关系并创建相应的填充数据的对象。这些新对象通过在 A 上调用 addBObject: 添加到 A。
因为解析器是循环的,解析 B 意味着解析 C,并且这里也创建了新对象并将其附加到 B。
这一切都发生在 MOC 3 上的 performBlock: 中。
- 解析(创建“A”对象并开始解析 B)
- 解析 A(创建“B”对象,将它们附加到 A 并开始解析 C)
- 解析 B(创建“C”对象,将它们附加到 B)
- 解析 C(仅将数据存储在 C 对象中)
- 解析 B(创建“C”对象,将它们附加到 B)
- 解析 A(创建“B”对象,将它们附加到 A 并开始解析 C)
在每次解析操作之后,我保存 MOC 3 并在 mainThread 上调度主 MOC (2) 的保存操作。由于NSManagedObjectContextDidSave 通知,MOC 1 将异步自动保存。
if (parsed){
NSError *error = nil;
if (![managedObjectContext save:&error])
NSLog(@"Error while saving parsed data: %@", error);
}else{
// something went wrong, discard changes
[managedObjectContext reset];
}
dispatch_async(dispatch_get_main_queue(), ^{
// save mainQueueManagedObjectContext
[[HWOverallDataController sharedOverallDataController] saveMainThreadManagedObjectContext];
});
为了释放我的内存占用,因为我现在不需要解析数据,所以我正在执行:
[a.managedObjectContext refreshObject:a mergeChanges:NO];
对于我刚刚解析的每个 A。
因为我需要解析大约 10 个 A,它们都有大约 10 个 B,它们大约有 10 个 C,因此会生成很多 managedObject。
问题 - 仪器
一切正常。唯一的事情是:当我打开分配工具时,我看到未发布的 A、B 和 C。我没有从他们的 retainCounts 或任何其他信息中获得任何有用的信息。
而且由于我的实际问题涉及更复杂的数据模型,因此活动对象成为严重的内存问题。
有人能弄清楚我做错了什么吗?使用正确的 managedObject 在其他 managedObjectContexts 上调用 refreshObjects 也不起作用。只有一个硬的reset 似乎可以工作,但随后我失去了指向 UI 使用的活动对象的指针。
我尝试过的其他解决方案
我尝试创建单向关系而不是双向关系。这会产生许多其他问题,导致 Core Data 不一致和奇怪的行为(例如悬空对象和 Core Data 生成 1-n 关系而不是 n-n 关系(因为反向关系未知)。
当我在任何对象上检索
NSManagedObjectContextDidSave通知时,我尝试刷新每个更改或插入的对象
这两个“解决方案”(顺便说一句,不起作用)似乎也有点老套。这不应该是要走的路。不过,应该有一种方法可以在不增加内存占用并保持 UI 流畅的情况下使其工作?
- 代码演示
- 进一步调查
在 mainContext 中(在 mainSave 之后)刷新每个曾经使用过的对象(这是一项繁琐的工作)后,对象的大小会减少到 48 个字节。这表明对象全部出错,但内存中仍有一个指针。当我们有大约 40.000 个对象都出现故障时,内存中仍然有 1.920 MB 的内存在persistentManagedObjectContext 重置之前永远不会释放。这是我们不想做的事情,因为我们失去了对任何 managedObject 的所有引用。
【问题讨论】:
-
这是一个很好的长描述,但如果没有代码,我怀疑你得到的不仅仅是猜测......
-
在我看来,与哪些代码非常相关不是问题。它更具概念性。但如果有必要,我可以制作一个小型演示项目,您可以在其中遇到问题。
-
我添加了一个说明问题的 XCode 项目
-
将导入/解析处理包装在 @autoreleasepool 块中,以便为每个对象 A(及其 B 和 C)清除池。
-
@Rog 不,可能不是这样。将它们包装在自动释放块之间并不能解决问题。经过更多调试后,mainManagedObjectContext (2) 似乎保持对 A 对象的强引用。