5

我想了解和修复我的应用程序效率低下。

我的算法是:

fetch object collection from network
for each object:
  if (corresponding locally stored object not found): -- A
    create object
    if (a nested related object locally not found): -- B
      create a related object

我正在通过使用属于我的模式的相关对象的键创建谓词查询来检查 A 和 B 行。我看到 A(总是)和 B(如果执行分支到该部分)都会生成一个 SQL 选择,例如:

2010-02-05 01:57:51.092 app[393:207] CoreData: sql: SELECT <a bunch of fields> FROM ZTABLE1 t0 WHERE  t0.ZID = ? 
2010-02-05 01:57:51.097 app[393:207] CoreData: annotation: sql connection fetch time: 0.0046s
2010-02-05 01:57:51.100 app[393:207] CoreData: annotation: total fetch execution time: 0.0074s for 0 rows.
2010-02-05 01:57:51.125 app[393:207] CoreData: sql: SELECT <a bunch of fields> FROM ZTABLE2 t0 WHERE  t0.ZID = ? 
2010-02-05 01:57:51.129 app[393:207] CoreData: annotation: sql connection fetch time: 0.0040s
2010-02-05 01:57:51.132 app[393:207] CoreData: annotation: total fetch execution time: 0.0071s for 0 rows.

0.0071s 的查询在 3GS 设备上是可以的,但如果你将其中的 100 个加起来,你就会得到一个 700ms 的拦截器。

在我的代码中,我使用了一个助手来进行这些获取:

- (MyObject *) myObjectById:(NSNumber *)myObjectId {
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    [fetchRequest setEntity:[self objectEntity]]; // my entity cache    
    [fetchRequest setPredicate:[self objectPredicateById:objectId]]; // predicate cache    
    NSError *error = nil;
    NSArray *fetchedObjects = [moc executeFetchRequest:fetchRequest error:&error];
    if ([fetchedObjects count] == 1) {
        [fetchRequest release];
        return [fetchedObjects objectAtIndex:0];
    }
    [fetchRequest release];
    return nil;
}

MyObject *obj = [self myObjectById];
if (!obj) {
   // [NSEntityDescription insertNewObjectForEntityForName: ... etc
}

我觉得这是错误的,我应该以其他方式进行检查。它应该只访问数据库一次,然后应该从内存中获取,对吗?(即使对于我确定在本地存在并且应该已经通过以前的查询加载到内存中的对象,SQL 也会执行。)但是,如果我只有来自外部源的 myObjectId,这是我能想到的最好的。

所以,也许问题是:如果我有 myObjectId(MyObject 上的 Core Data int64 属性),我应该如何正确检查 CD 存储中是否存在相关的本地对象?预加载整组可能的匹配,然后断言一个本地数组?

(一种可能的解决方案是将其移至后台线程。这很好,除了当我从线程获取更改并执行 [moc mergeChangesFromContextDidSaveNotification:aNotification]; (通过通知从后台线程获取更改的对象)时,这仍然阻塞。)

4

6 回答 6

9

阅读 Core Data Programming Guide 中的“Implementing Find-or-Create Efficiently”。

基本上,您需要创建一组 ID 或属性,例如名称,或者您从托管对象实体中拥有的任何内容。

然后您需要创建一个谓词,该谓词将使用此数组过滤托管对象。

[fetchRequest setPredicate:[NSPredicate predicateWithFormat: @"(objectID IN %@)", objectIDs]];

当然,“objectIDs”可以是任何你可以用来识别的东西。它不必是 NSManagedObjectID。

然后你可以做一个获取请求,并迭代生成的获取对象,以查找重复项。如果它不存在,请添加一个新的。

于 2010-02-05T20:04:43.997 回答
3

您可能可以从电子邮件客户端中吸取教训。

它们通过首先向服务器查询消息 ID 列表来工作。一旦客户端拥有该列表,它就会与它的本地数据存储进行比较,看看是否有任何不同。

如果存在差异,则需要采取一些措施之一。1.如果它存在于客户端,但不存在于服务器并且我们是IMAP,则在本地删除。2. 如果它存在于服务器上,但不存在于客户端,则下载剩余的消息。

在您的情况下,首先查询所有 ID。然后发送后续查询以获取您尚未拥有的数据的所有数据。

如果您的记录可能存在于本地,但自上次下载到服务器后可能已更新,那么您的查询应包括上次更新日期。

于 2010-02-05T18:02:15.813 回答
3

您应该对所有对象进行一次获取,但只获取对象的服务器 ID。

使用setPropertiesToFetch:setResultType:设置为NSDictionaryResultType.

于 2010-02-05T19:34:39.247 回答
1

听起来您需要的是 NSManagedObjectIDs 的 NSSet,它被加载到内存中或存储在比持久对象存储更快访问的地方。

这样,您可以将网络中的对象 ID 与缓存中的对象 ID 进行比较,而无需对大型数据集执行获取请求。

也许从托管实体类中的 -awakeFromInsert 将 ID 添加到缓存中?

于 2010-02-05T18:24:25.357 回答
1

在与同样的问题斗争了多年之后,我终于偶然发现了这篇博客文章,它完全解决了它(并且是一个可重用的代码块作为奖励!)。

http://henrik.nyh.se/2007/01/importing-legacy-data-into-core-data-with-the-find-or-create-or-delete-pattern

虽然代码示例没有涵盖网络部分;您只需将其加载到 NSDictionary 中。然后处理本地核心数据上下文的同步。

于 2010-02-18T05:02:45.550 回答
0

不是答案,而是“有效导入数据”文档的更新 URL

http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreData/Articles/cdImporting.html#//apple_ref/doc/uid/TP40003174-SW1

于 2011-04-17T00:41:29.937 回答