核心数据中的位置按NSFetchedResultsController的距离排序?

我的iOS Core Data数据库中有一组实体对象,用于描述某个位置的内容。 我们称实体位置。 我已经通过在Location上有两个属性来实现这一点,这两个属性引用了位置 – 纬度和经度,两个都是双倍的。 还有其他元素,如名称。

我使用NSFetchedResultsController将实体绑定到UITableViewController。 我想要做的是将结果按距离分配到给定的CLLocationCoordinate2D。 在一个非常理想的场景中,我能够刷新该列表以基于新位置重新计算排序。 因此,这种类型将依赖于两个键,以及第三个“静态”变量(一个在集合中的项目之间不变的变量)。

如果我用NSSortDescriptors排序任意列表,我想我可以弄清楚如何做到这一点。 但是,我不控制NSFetchedResultsController中如何使用排序描述符。

有没有办法可以配置我的实体,我的NSFetchedResultsController,我的NSSortDescriptors等来完成这个? 我怀疑答案不在于创建一个花哨的NSSortDescriptor,而是在实体中创建一个瞬态属性来表示与我的距离,并定期重新计算该属性。 但是,我对核心数据足够新,我不确定如何做到这一点(迭代所有实体并重新计算字段)。 我也不确定NSSortDescriptors是否可以处理Transient属性。

(来自评论:)

对(基于SQLite的)Core Data存储的获取请求不能使用基于瞬态属性或基于Objective-C的谓词的排序描述符。

如果您不想失去获取结果控制器的优点(如动画表视图更新,自动分组到部分等),您必须预先计算到当前位置的距离并将其存储在(持久)属性中你的对象

或者,您可以获取所有对象并在内存中对它们进行排序。 在这种情况下,您可以使用任意排序描述符。 但是这不能与获取的结果控制器结合使用,因此您必须在托管对象上下文中注册更改并在必要时重新加载表。

我发现了BSFetchedResultsController github项目 ,它是一个NSFetchResultsController子类,它执行Martin建议的内容,它使用任意排序描述符在内存中排序,此外它还注册了上下文的更改并再次计算任何索引更改,同时考虑到任意排序描述符。 总而言之,这是一项令人印象深刻 我成功地使用它按距离排序如下:

BSFetchedResultsController* fetchedResultsController = [[BSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:nil]; // create a location to compare distance to, eg current location CLLocation* sourceLocation = [[CLLocation alloc] initWithLatitude:55.87595153937809 longitude:-4.2578177698913855]; // compare the distance from both to the source location fetchedResultsController.postFetchComparator= ^(id a, id b) { Venue* v1 = (Venue*)a; Venue* v2 = (Venue*)b; double d1 = [v1.coreLocation distanceFromLocation:sourceLocation]; double d2 = [v2.coreLocation distanceFromLocation:sourceLocation]; return [@(d1) compare:@(d2)]; }; NSError *error = nil; if (![fetchedResultsController performFetch:&error]) { NSLog(@"Unresolved error %@, %@", error, [error userInfo]); } 

由于它是一个旧项目,它没有ARC,所以当你包含这两个文件时,记得在Target,Build Phases中使用Compiler Flags -fno-objc-arc标记.m。 还要注意开发人员认为代码不是生产就绪,所以如果使用它,请确保进行充分的测试。

在上面的代码中,我在Venue托管对象子类上有一个瞬态属性coreLocation,你可以在这里看到如何实现它。 此外,距离计算效率低下,您可能希望缓存对象中的距离,而不是每次比较时重新计算距离。

最后,看来这个项目是因为创造者丹尼尔索普的Stackoverflow问题没有得到解决,导致他解决问题并自己发布唯一的答案,所以我想如果你发现他的项目有用,你可以亲切地投票他的post就像我一样。