我目前正在为我的项目使用coredata。但是当api返回应用程序需要更新的54000个对象时,用户必须等待将近2个小时。 这是当前项目的主要问题,我正在考虑使用sqlite而不再使用coredata来更新数千个对象。
使用Sqlite是正确的决定还是对CoreData有任何建议?我无法决定。任何帮助都会很棒。谢谢。
这是我正在做的事情:
NSManagedObjectContext *privateObjectContext = [AppDelegate appDelegate].privateManagedObjectContext;
[privateObjectContext performBlock:^{
int i = 1;
for (NSDictionary *item in itemlist) {
i++;
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:
@"itemID == %@",[item objectForKey:@"item_id"]
]];
NSError *error;
NSMutableArray *inventories = [[NSMutableArray alloc]initWithArray:
[privateObjectContext executeFetchRequest:fetchRequest
error:&error]];
ItemManagedObject *itemMO;
if(inventories.count){
itemMO = inventories.firstObject;
}else{
itemMO = [NSEntityDescription insertNewObjectForEntityForName:@"ItemObject"
inManagedObjectContext:privateObjectContext];
}
[itemMO prepareWithDictionary:item];
}
NSError *error;
if (![privateObjectContext save:&error]) {
completionHandler(NO);
}
}
答案 0 :(得分:1)
Core Data提供NSBatchUpdateRequest
,它允许您直接在持久存储上进行更新,而不涉及在内存中实例化和处理托管对象。
您也应该使用核心数据性能工具运行此代码。如果itemList
包含54,000个对象,那么您每次都会对持久性存储执行54,000次提取以检查单个ID。预先获取所有ID然后检查内存中的结果比执行重复获取请求要快得多 - 该原始SQL中的代码几乎与Core Data中的代码一样慢。
此代码也看起来不对:
ItemManagedObject *itemMO;
if(itemMO.count){
如果测试,它永远不会通过,除非你错过了某个地方的线路。
答案 1 :(得分:0)
我从未在sqlite中重做核心数据项目,反之亦然。所以我不能告诉你是否存在性能差异/
然而,54k = 2小时的事情听起来很奇怪。你谈到一个让我怀疑服务器涉及的API,你的问题是关于数据库。当然2个小时听起来太长了,让我想知道你是否对数据库的核心设计有疑问。例如,缺乏索引。根据您的查询和数据库,单个更新可能会触发各种重载处理。
另一个原因是您在设备上处理此列数据的原因。要处理的事情很多,我想知道是否有办法减少音量,有选择地做更新,甚至更好 - 将它移到服务器上。
我认为你需要重新考虑你的问题。提供有关数据库的更多上下文,确切地说您正在使用它以及为什么这样做。
答案 2 :(得分:0)
CoreData不是数据库管理器,而是对象图和持久性管理器。 CoreData可以将其对象存储在sqlite数据库中,也可以存储在XML文件或二进制文件中(开发人员可以选择最适合其需求的选项)。
CoreData和数据库管理器之间的主要区别在于,要使用CoreData访问对象,CoreData需要实例化Objective-C / Swift对应的对象。
Sqlite可以访问部分数据,而无需提取包含数据的完整记录。
然后,CoreData需要维护对象之间的关系图(两个CoreData类之间的关系,一般来说,两种方式)。
因此,在更新54k对象时,您要求CoreData实例化54k对象(在内存中)并最终更新它们的关系。
对于移动设备上的CoreData来说,这是非常繁重的工作。
可能您的CoreData模型未正确优化。 也许您应该定期保存CoreData上下文并刷新CoreData暂存器(包含实际读取或更新对象的内存部分)。
但根据我的经验,CoreData不适合繁重的数据工作。
如果您希望能够从sqlite记录中重新实例化classe对象并管理相当自动的关系,那么使用sqlite重新实现您的需求可能会非常有用,但它是可行的。我在一些项目上做过。这样可以增加一个模型对象,例如Android可以与其他平台共享,因为sqlite可以在很多平台上使用。
还有一件事:sqlite更适合从多个线程使用。 CoreData对此更加敏感,并且需要一个线程上下文,最终需要一些上下文同步。
答案 3 :(得分:0)
2小时很长。这很奇怪。
然而,您可以通过让核心数据减少工作来按摩您的代码。工作少得多。
这将大大减少Core Data执行的工作量以及应用程序的性能。
第二点很简单,但非常冗长:在调用setter之前将每个单独的属性值与字典值进行比较。
第一点需要更改算法:
执行单个获取请求,按id排序(使用[NSFetchRequest setSortDescriptors:])
按ID排序词典(使用[NSArray sortedArray ...])
同步两个已排序的列表(两个列表的排序至关重要):
NSEnumerator *itemMOEnum = [itemMOs objectEnumerator];
NSEnumerator *dicEnum = [dictionaries objectEnumerator];
ItemManagedObject *itemMO = [itemMOEnum nextObject];
NSDictionary *itemDic = [dicEnum nextObject];
while (itemDic) {
NSComparisonResult comparison = itemMO ? [itemDic[@"item_id"] compare:itemMO.itemID] : NSOrderedAscending;
switch (comparison) {
case NSOrderedSame:
// id present in both lists: update
[itemMO prepareWithDictionary:itemDic];
itemMO = [itemMOEnum nextObject];
itemDic = [dicEnum nextObject];
break;
case NSOrderedAscending: {
// id present only in dictionaries: create
itemMO = [NSEntityDescription insertNewObjectForEntityForName:@"ItemObject"
inManagedObjectContext:privateObjectContext];
[itemMO prepareWithDictionary:itemDic];
itemDic = [dicEnum nextObject];
} break;
case NSOrderedDescending:
// id present only in managed object: delete or do nothing
itemMO = [itemMOEnum nextObject];
break;
}
}
while (itemMO) {
// id present only in managed object: delete or do nothing
itemMO = [itemMOEnum nextObject];
}
并保存。
最后,也许SQLite会更快(请参阅https://github.com/groue/GRDB.swift/wiki/Performance尝试比较Core Data与SQLite库的性能)。
但SQLite不会将慢速算法变成快速算法。