使用SQLite后端修复谓词NSFetchedResultsController / NSFetchRequest性能?

时间:2010-02-04 05:25:43

标签: iphone performance core-data nsfetchedresultscontroller

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

我调查并证明谓词是问题所在:

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

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

当我删除用于测试的谓词时,获取时间(初始performFetch:call)从4秒降低(对于某些极端情况)到大约100ms或更短,这是可以接受的。我对此感到困扰,因为它否定了我希望通过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个对象的谓词查询,二进制存储性能始终为~100ms。)

(我先前在这里嵌套了另一个问题,我现在moved away)。

3 个答案:

答案 0 :(得分:4)

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

通过将谓词应用于多对多关系,并非所有应用都是狗慢,所以还有更多内容。见Apple's performance document。设置-com.apple.CoreData.SQLDebug 1.

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

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

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

我不会像将Object Objects包装到属性中那样创建自己的to-many外键引用。听起来像一个可怕的黑客肯定会阻止你认真使用核心数据......但核心数据的优势很多。

尝试删除或添加反向关系。我在一个案例中发现,如果没有反向关系,我的查询运行得更快。缺点是删除反向关系使我的数据库膨胀。

让我们知道您的发现。

答案 1 :(得分:0)

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

答案 2 :(得分:0)

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

由于您的关系是双面的(正确?),您可以从另一方面来看它,在这种情况下您可能根本不需要NSFetchedResultsController。假设你有:

MyWidgetEntity&lt; ---&gt;&gt; SomethingEntity

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

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

在你有以下情况:

MyWidgetEntity&lt;&lt; - &gt;&gt; SomethingEntity

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

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

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