【发布时间】:2016-04-10 13:10:46
【问题描述】:
我在使用 Core Data 和 NSArrayController 的 OS X 应用程序中遇到了一些奇怪的行为。每个 fetch: 发送到数组控制器的消息都会导致从持久性存储 (sqlite) 进行 两次 fetch,当然,这会花费两倍的时间来进行 fetch。
当我设置数组控制器的 fetchPredicate 属性然后发送一个 fetch: 消息时会发生这种情况。
我使用标准的苹果核心数据应用程序模板创建了一个简单的测试应用程序,其中一个实体“SomeEntity”只有一个属性“stringAttribute”。我继承了 NSArrayController 并覆盖了 fetchWithRequest:merge:error: 以设置断点并查看数组控制器何时从存储中获取。
然后,我编写了一个仅包含两行的简单操作方法(连接到按钮):
self.arrayController.fetchPredicate = [NSPredicate predicateWithFormat:@"TRUEPREDICATE"];
[self.arrayController fetch:self];
而且,每次我按下按钮时,fetchWithRequest:merge:error: 方法都会被调用两次,导致两次访问持久性存储。
NSArrayController 继承自实现 fetch: 方法的 NSObjectController。文档说
从 OS X v10.4 开始,此方法的结果将延迟到 runloop 的下一次迭代,以便错误呈现机制可以以表格形式提供反馈。
fetchWithRequest:merge:error: 确实由运行循环机制中触发的 NSInvocation 调用。经过一点断点设置后,我发现一个 NSInvocation 是由
行创建的self.arrayController.fetchPredicate = [NSPredicate predicateWithFormat:@"TRUEPREDICATE"];
下一行还有第二个
[self.arrayController fetch:self];
当我注释掉第二行时,控制器根本不会获取。因此,只要我想在获取之前设置获取谓词,我就会得到两次获取。奇怪的是,当我在应用启动后第一次按下按钮时不会发生这种情况。
这感觉很奇怪,因为我基本上只在标准 Core Data 堆栈中添加了两行代码。当我用测试数据填充存储并测量 fetchWithRequest:merge:error: 完成所需的时间时,我可以清楚地看到 fetch 发生两次并花费两倍时间的性能问题。
直接调用 fetchWithRequest:merge:error: 不是一个理想的解决方案,因为 NSArrayController 中有一些自动功能,我必须自己重新实现。不应该是首先设置 fetchPredicate 而不是简单地调用 fetch: 的正确方法吗?
有什么想法吗?
【问题讨论】:
-
arrayController.automaticallyPreparesContent 开启了吗?
-
不,不是,而是有趣的问题。当我打开它时,它会在 XIB 加载后立即获取两次!所以我们看到 fetchWithRequest:merge:error: 调用了两次(并且花费了两倍的时间),即使我们根本不编写任何代码!
标签: objective-c macos core-data