3

我有一系列 NSFetchedResultsController 为一些表视图提供动力,它们在设备上的性能非常糟糕,大约为几秒。由于它全部在主线程上运行,它在启动时阻塞了我的应用程序,这不是很好。

我调查并发现谓词是问题所在:

NSPredicate *somePredicate = [NSPredicate predicateWithFormat:@"ANY somethings == %@", something];
[fetchRequest setPredicate:somePredicate];

即获取实体,称为“事物”,与实体“事物”具有多对多关系。该谓词是一个过滤器,它将结果限制为仅与特定“某物”有关系的事物。

当我删除谓词进行测试时,获取时间(初始 performFetch: 调用)从 4 秒(对于某些极端情况)下降到大约 100 毫秒或更短,这是可以接受的。不过,我对此感到困扰,因为它否定了我希望通过 Core Data 和 NSFRC 获得的很多好处,否则这似乎是一个强大的工具。

所以,我的问题是,如何优化这种性能?我使用谓词错误吗?我应该以某种方式修改模型/模式吗?还有什么其他方法可以解决这个问题?这种性能下降是可以预料的吗?(大约有数百个 <1KB 的对象。)

编辑细节:

这是代码:

[fetchRequest setFetchLimit:200];
NSLog(@"before fetch");
BOOL success = [frc performFetch:&error];
if (!success) {
    NSLog(@"Fetch request error: %@", error);
}
NSLog(@"after fetch");

更新的日志(以前,我有一些应用程序效率低下,降低了性能。这些更新的日志应该尽可能接近最佳状态,因为你可以在我当前的环境下获得):

2010-02-05 12:45:22.138 Special Ppl[429:207] before fetch
2010-02-05 12:45:22.144 Special Ppl[429:207] CoreData: sql: SELECT DISTINCT 0, t0.Z_PK, t0.Z_OPT, <model fields> FROM ZTHING t0 LEFT OUTER JOIN Z_1THINGS t1 ON t0.Z_PK = t1.Z_2THINGS WHERE  t1.Z_1SOMETHINGS = ? ORDER BY t0.ZID DESC LIMIT 200
2010-02-05 12:45:22.663 Special Ppl[429:207] CoreData: annotation: sql connection fetch time: 0.5094s
2010-02-05 12:45:22.668 Special Ppl[429:207] CoreData: annotation: total fetch execution time: 0.5240s for 198 rows.
2010-02-05 12:45:22.706 Special Ppl[429:207] after fetch

如果我在没有谓词的情况下进行相同的提取(通过注释掉问题开头的两行):

2010-02-05 12:44:10.398 Special Ppl[414:207] before fetch
2010-02-05 12:44:10.405 Special Ppl[414:207] CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT, <model fields> FROM ZTHING t0 ORDER BY t0.ZID DESC LIMIT 200
2010-02-05 12:44:10.426 Special Ppl[414:207] CoreData: annotation: sql connection fetch time: 0.0125s
2010-02-05 12:44:10.431 Special Ppl[414:207] CoreData: annotation: total fetch execution time: 0.0262s for 200 rows.
2010-02-05 12:44:10.457 Special Ppl[414:207] after fetch

时间相差20倍。500ms 并不是那么好,而且似乎没有办法在后台线程中完成它或以其他方式优化我能想到的。(除了去二进制存储这成为一个非问题,所以我可能会这样做。对于上述 200 个对象的谓词查询,二进制存储性能始终为 ~100 毫秒。)

(我之前在这里嵌套了另一个问题,现在我搬走了)。

4

3 回答 3

4

对于我们中的许多人来说,这是一个持续存在的问题,他们试图在 iPhone 上使用 Core Data 来处理更大的数据库或更复杂的模式。我的个人资料有几个与此相关的不同 SO 问题的链接——在全文搜索的上下文中。

并非所有应用程序都只是通过将谓词应用于一对多关系而变得缓慢,因此还有更多。请参阅Apple 的性能文档。设置 -com.apple.CoreData.SQLDebug 1。

如果合适,检查是否有索引。确保没有调用使 sqlite 无法有效加入或使用索引的 sql 函数(例如格式/类型转换)。

如果您有很多行并且实体中有很多属性,则可能存在内存问题。考虑将单个实体拆分为一个较小的实体,其中包含一些用于搜索的属性,并在需要时提取其余部分。

如果您的谓词比对多关系部分更复杂,那么您可以尝试分两步进行搜索。例如,SQL 可能会低效地加入然后过滤而不是过滤然后加入。但是这样做会破坏 NSFetchedResultsController。不过,它可能会揭示一些麻烦。

我不会做诸如将 ObjectID 打包到属性中来创建自己的多外键引用之类的事情。听起来像是一个可怕的 hack,肯定会阻止你认真使用 Core Data……但 Core Data 的优点很多。

尝试删除或添加反向关系。我发现在一种情况下,我的查询在没有反向关系的情况下运行得更快。缺点是删除反向关系会使我的数据库膨胀。

让我们知道你发现了什么。

于 2010-02-04T07:36:19.980 回答
0

对我来说,实际的解决方案是切换到性能更好的 Core Data 二进制存储。我没有调查是否可以根据我当前的应用情况来提高 Core Data SQLite 的性能。

于 2010-03-31T16:10:04.927 回答
0

二进制故事会将整个数据集加载到内存中,这将导致某些设备出现内存问题。相反,您应该重新编写谓词。

由于您的关系是双面的(吗?),您可以从另一面开始,并且在这种情况下您可能根本不需要NSFetchedResultsController。假设你有:

MyWidgetEntity <--->> SomethingEntity

由于您已经引用了 SomethingEntity 的实例,因此您只需通过以下方式询问它的小部件:

id widget = [mySomethingInstance valueForKey:@"widget"];

在您有以下情况的情况下:

MyWidgetEntity <<-->>SomethingEntity

您可以通过以下方式访问所有相关的小部件:

NSSet *widgets = [mySomethingInstance valueForKey:@"widgets"];

不需要NSFetchedResultsController,因为您可以将此集合转换为数组并对其进行排序。

于 2010-03-31T16:46:50.003 回答