Core Data 在 iPhone4 等旧设备上非常慢

Posted

技术标签:

【中文标题】Core Data 在 iPhone4 等旧设备上非常慢【英文标题】:Core Data is very slow on the old device such as iPhone4 【发布时间】:2016-01-29 14:25:19 【问题描述】:

在我的应用程序中,我使用了简单的核心数据结构。 我调用我的服务器 api,然后将每个请求存储 30 个对象到NSManagedObject。这样每个周期有 30 个托管对象,然后我的 tableView 重新加载。

我的代码是这样的

//we have 30 entities here in "post_items"
for(NSDictionary *post in [d objectForKeyNotNull:@"post_items"])
    
        NSNumber *itemId= [post objectForKeyNotNull:@"id"];
    if(itemId)


        Item *i = [[ChannelStore sharedStore] fetchItem:itemId];

        if(!i) i = [[ChannelStore sharedStore] createItem];

        [i readFromJSONDictionary:post];

        [_items addObject:i];


    




-(Item*)createItem

    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Item" inManagedObjectContext:self.managedObjectContext];

Item *item = [[Item alloc] initWithEntity:entity insertIntoManagedObjectContext:self.managedObjectContext];

return item;




   -(Item*)fetchItem:(NSNumber *)itemId;




NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"Item"];

// Create Predicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%K == %@", @"itemID", itemId];
[fetchRequest setPredicate:predicate];

[fetchRequest setFetchBatchSize:1];

// Execute Fetch Request
NSError *fetchError = nil;


NSArray *result = [self.managedObjectContext executeFetchRequest:fetchRequest error:&fetchError];
Item* item = nil;

if (!fetchError) 
    item = [result firstObject];

 else 
    NSLog(@"Error fetching data.");
    NSLog(@"%@, %@", fetchError, fetchError.localizedDescription);



return item;

是什么让这个过程变慢是因为我必须先获取所以检查现有的项目,如果没有,然后创建新的。 我知道我在这里只使用了一个 MOC。这会导致 UI 阻塞。但是,如果我们在这里关心速度。我怀疑实施亲子 MOC 会有所帮助。因为在 parent-child 。我只会在后台运行另一个 MOC,但它是一个用于 30 个对象的 MOC,它或多或少应该花费相同的时间。

任何帮助将不胜感激。我尝试到处搜索,但找不到合适的解决方案。或者,我根本不应该将核心数据用于“项目”对象?

PS。我在较新的设备上没有性能问题,只是旧设备使我的应用看起来非常滞后和缓慢

【问题讨论】:

你应该有一个背景上下文。将 id 存储在内存中以确定是否存在欺骗而不是单独获取每个,或批量请求 您可以通过最小化您拨打的电话数量来加快速度 - 在所有设备上。在开始检查重复项之前获取所有 30 个对象,并且您只有一个 fetch 开销而不是 30 个。还值得记住的是,需要更长的时间,但看起来更快的东西 - 使用后台获取和快速更新 UI - 给出了一个更好的用户体验。如果您在开始更新显示之前不需要所有返回的数据,那么不要等待!去后台获取剩下的数据 @Russell 谢谢,我终于通过只进行一次提取而不是 30 次提取来解决问题。该应用程序现在看起来非常快。但是,偶尔,应用程序会非常随机地冻结 2-3 秒。你知道为什么吗? 【参考方案1】:

您在这里运行缓慢的主要原因是您正在执行的 fetch 数量。每个获取请求都会强制核心数据堆栈一直往返到磁盘,因为您正在为要导入的 每个 项目执行此操作,这会导致您的导入实现出现瓶颈。这个瓶颈会导致 UI 变慢,因为它发生在主队列上下文中。

解决方案是执行一次提取,以查看哪些传入的对象已经在本地,哪些是新的。

在内存中拥有本地对象后,检查该数组比每次使用NSFetchRequest 快得多。

这是一个 WWDC 视频,可以解决您的确切问题:https://developer.apple.com/videos/play/wwdc2013-211/

他们特别谈到在视频中大约 14m 处实施“更新或插入”。

【讨论】:

谢谢。我已经按照你的建议看完了视频。无论如何,您是否有使用 NSEnumerator 进行“更新或插入”操作的示例代码? .视频中只看到了几行代码,没有看到全部。 没关系,我想出了解决方案。只需使用 %@ 中的 %K 等 SQL 语法。而 %@ 是 nsarray 该应用程序现在看起来非常快。但是,偶尔,应用程序会非常随机地冻结 2-3 秒。你知道为什么吗? 没有看到更多代码,我无法在那里进行任何真正的调用。最好的办法是查看一些关于分析核心数据的 WWDC 视频,您应该能够准确找到导致问题的原因。【参考方案2】:

您可以通过要求CodeData 仅获取感兴趣的属性(而不是获取所有属性)来优化您的NSFetchRequests。

所以,如果您只对itemID 感兴趣,那么请CoreData 只检查itemIDs

theFetchRequest.predicate = ...;
// you MUST set the predicate BEFORE using the next lines, otherwise your app will crash

theFetchRequest.includesSubentities = NO;
theFetchRequest.propertiesToFetch = @[@"itemID"];

当您的实体拥有

时,这对有很大帮助 NSData 类型的属性 与其他实体的关系

几天前,这两行代码让我的代码快了 400 倍...

【讨论】:

【参考方案3】:

您必须使用 privatequeueContext 从服务器和 mainQueuCONtext 获取数据来处理 UI 部分。欲了解更多信息,请访问以下链接

CoreDataConcurrency_Swift

CoreDataCConcurrency_ObjC

【讨论】:

以上是关于Core Data 在 iPhone4 等旧设备上非常慢的主要内容,如果未能解决你的问题,请参考以下文章

iPhone4 Core Data如何插入带有节号的新对象?

Core Data 无法删除从未插入的对象错误

使用 iCloud 在多台设备上同步 Core Data

Core Data + CloudKit 不会在其他设备上自动刷新?

iPhone 4 iOS 5 如何使用 Core Data 保存 MPMediaItem?

Fetch on Core Data 可以在模拟器上运行,但不能在设备上运行