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
仅获取感兴趣的属性(而不是获取所有属性)来优化您的NSFetchRequest
s。
所以,如果您只对itemID
感兴趣,那么请CoreData
只检查itemID
s
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 + CloudKit 不会在其他设备上自动刷新?