带有函数参数的 CoreData 谓词

Posted

技术标签:

【中文标题】带有函数参数的 CoreData 谓词【英文标题】:CoreData Predicate with Function Argument 【发布时间】:2019-04-02 04:36:21 【问题描述】:

我试图在谓词定义中包含一个函数。这可能吗?

假设您有一个 Places 的核心数据实体,其属性为 纬度和经度。

我想在一个地图视图中添加注释 与用户位置的指定距离。当然,我可以遍历整个数据库并计算每个 Place 和 用户位置,但我将有大约 35000 个位置,看起来 在 fetchedResultsController 设置。

我尝试了下面的代码,但收到一条错误消息“问题与 子谓词 BLOCKPREDICATE(0x2808237b0),userInfo 为 (null)"

func myDistanceFilter(myLat : CLLocationDegrees, myLong : CLLocationDegrees, cdLat : CLLocationDegrees, cdLong : CLLocationDegrees) -> Bool 

    let myLocation = CLLocation(latitude: myLat, longitude: myLong)
    let cdLocation = CLLocation(latitude: cdLat, longitude: cdLong)

    if myLocation.distance(from: cdLocation) < 5000.0 
        return true
     else 
        return false
    
//myDistancePredicate

在 fetchedResultsController 内部:

let distancePredicate = NSPredicate _,_ in self.myDistanceFilter(myLat: 37.774929, myLong: -122.419418, cdLat: 38.0, cdLong: -122.0)

如果可以在谓词中包含块/函数,您如何获取对正在评估的实体对象的属性的引用?

任何指导将不胜感激。

【问题讨论】:

您要为此获取所有匹配值吗? 是的。获取匹配项,填充表格视图并使用结果将注释添加到地图视图。 与其尝试定义您希望注释落入的半径(正如 Jerry 在下面指出的那样,不能用于获取),而是设置最小和最大纬度和经度。然后,您的谓词将是“latitude > minLat AND latitude minLong AND longitude 【参考方案1】:

对遇到类似问题的其他人的补充观察。

考虑到上面 pbasdf 和 Jerry 的建议,至少在我的情况下,一个区域没有理由必须是圆形的。我将制作一个名称,表示一个几乎是矩形的区域。我用纬度和经度值进行了一些测试。这些纬度和经度值可以缩放为包含用户指定的圆形半径的矩形。纬度 1 度约为 69 英里,芝加哥纬度的 1 度经度约为 51 英里。我使用了以下内容:

var myUserLatitude : CLLocationDegrees!
var myUserLongitude : CLLocationDegrees!

在视图的初始化文件中:

guard let userLatitude = locationManager.location?.coordinate.latitude else return
guard let userLongitude = locationManager.location?.coordinate.longitude else return
myUserLatitude = userLatitude
myUserLongitude = userLongitude

内部 fetchedResultsController 变量创建:

let latitudeMinPredicate = NSPredicate(format: "latitude >= %lf", myUserLatitude - 1.0)
let latitudeMaxPredicate = NSPredicate(format: "latitude <= %lf", myUserLatitude + 1.0)
let longitudeMinPredicate = NSPredicate(format: "longitude >= %lf", myUserLongitude - 1.0)
let longitudeMaxPredicate = NSPredicate(format: "longitude <= %lf", myUserLongitude + 1.0)

var compoundPredicate = NSCompoundPredicate()
compoundPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [anotherUnrelatedPredicate,latitudeMinPredicate,latitudeMaxPredicate,longitudeMinPredicate, longitudeMaxPredicate])
fetchRequest.predicate = compoundPredicate        

显然,我将创建另一个属性来缩放每个用户所需区域的 1.0 值。初步测试似乎有效,最重要的是我无法相信它有多快。从字面上看,tableView 是在 viewController 连接到封闭的 mapView 时填充的。

【讨论】:

【参考方案2】:

嗯,是的,可能NSPredicate 确实有 +predicateWithBlock:init(block:)

但是,如果您在该页面上向下滚动,您会看到坏消息:

Core Data 在内存和原子存储中支持基于块的谓词,但在基于 SQLite 的存储中不支持。

因此,无论您是使用内存存储,还是在代码中进行过滤,都需要将这 35,000 个项目放入内存,而蛮力方法的性能会很差。

有一点复杂性,SQL 不再适用——使用真实代码可以获得更好的性能。我认为您的要求远远超出了这一点。这将是一个有趣的优化计算机科学项目。您需要进行一些预计算,类似于将 index 添加到数据库中。考虑将region 属性添加到您的place 实体,然后编写您的谓词以获取目标位置区域内的所有地点和所有直接邻居。然后过滤代码中的那些候选人。我确信其他人已经做到了这一点——想想手机网络中的单元——但 Core Data 不会免费为您提供

【讨论】:

明白。我会寻找预处理的方法。我最初采用区域方法并按州进行细分。这工作得很好,但当然,如果你想要在 100 英里内的地方,并且用户在内华达州里诺市,那么你不会在加州几英里外的地方获得任何点击。谢谢。 是的,你明白了。您的 Reno/California 示例就是为什么所有直接邻居都必须是候选人的原因。在数学上理想的世界(没有山、水或城市)中,您的区域将被塑造为六边形,每个区域将有六个直接相邻的区域。你会玩得开心的:)

以上是关于带有函数参数的 CoreData 谓词的主要内容,如果未能解决你的问题,请参考以下文章

将 UITextField 中的文本用于 fetchRequest 谓词

谓词函数函数对象

STL学习思想

CoreData 的谓词查询

coredata中谓词的使用

SwiftUI在FetchRequest中使用带有结构参数的谓词