根据 Apple 的文档(链接)—</p>
在许多情况下,您可能需要为一组离散输入值查找现有对象(已保存在商店中的对象)。一个简单的解决方案是创建一个循环,然后对每个值依次执行一次 fetch 以确定是否存在匹配的持久化对象等。这种模式不能很好地扩展。如果您使用此模式分析您的应用程序,您通常会发现 fetch 是循环中更昂贵的操作之一(与仅迭代项目集合相比)。更糟糕的是,这种模式将
O(n)
问题变成了O(n^2)
问题。在可能的情况下,一次创建所有托管对象,然后在第二次处理中修复任何关系会更有效。例如,如果您导入的数据您知道不包含任何重复项(例如,因为您的初始数据集为空),您可以只创建托管对象来表示您的数据,而根本不进行任何搜索。或者,如果您导入没有关系的“平面”数据,您可以为整个集合创建托管对象,并在使用单个大
IN
谓词保存之前清除(删除)任何重复项。
问题1:考虑到我正在导入的数据没有任何关系,我如何实现最后一行中描述的内容。
如果您确实需要遵循查找或创建模式(例如,因为您要导入异构数据,其中关系信息与属性信息混合在一起),您可以通过将获取的次数减少到最少来优化查找现有对象的方式执行。如何实现这一点取决于您必须使用的参考数据量。如果您要导入 100 个潜在的新对象,而您的数据库中只有 2000 个,那么获取所有现有对象并缓存它们可能不会造成重大损失(特别是如果您必须多次执行该操作)。但是,如果您的数据库中有 100,000 个项目,那么保持这些缓存的内存压力可能会令人望而却步。
您可以结合使用 IN 谓词和排序来将 Core Data 的使用减少到单个 fetch 请求。
示例代码:
// Get the names to parse in sorted order.
NSArray *employeeIDs = [[listOfIDsAsString componentsSeparatedByString:@"\n"]
sortedArrayUsingSelector: @selector(compare:)];
// create the fetch request to get all Employees matching the IDs
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:
[NSEntityDescription entityForName:@"Employee" inManagedObjectContext:aMOC]];
[fetchRequest setPredicate: [NSPredicate predicateWithFormat: @"(employeeID IN %@)", employeeIDs]];
// Make sure the results are sorted as well.
[fetchRequest setSortDescriptors:
@[ [[NSSortDescriptor alloc] initWithKey: @"employeeID" ascending:YES] ]];
// Execute the fetch.
NSError *error;
NSArray *employeesMatchingNames = [aMOC executeFetchRequest:fetchRequest error:&error];
您最终会得到两个排序的数组——一个包含传递到获取请求中的员工 ID,另一个包含与它们匹配的托管对象。要处理它们,请按照以下步骤遍历排序列表:
获取下一个 ID 和 Employee。如果 ID 与员工 ID 不匹配,请为该 ID 创建一个新员工。获取下一个员工:如果 ID 匹配,则移动到下一个 ID 和员工。
问题2:在上面的例子中,我得到了两个如上所述的排序数组。考虑到所有要插入的对象都存在于商店中的最坏情况,无论如何我看不到我可以及时解决问题O(n)
。Apple 描述了上述两个步骤,但这是一项O(n^2)
工作。对于输入数组中的任何kth
元素,在输出数组的第一个元素中可能存在也可能不存在与其匹配的k
元素。所以在最坏的情况下,复杂度将是O(nC2) = O(n^2)
.
所以,我相信苹果正在做的是确保只处理一次,即使O(n^2)
需要检查。如果是这样,那么我会这样做;但是有没有其他方法可以有效地做到这一点。
请理解,我不想一次又一次地获取 - 为大小为 100 个标识符的输入数组获取一次。