CLLocation 如何实现 Equatable 协议?

Posted

技术标签:

【中文标题】CLLocation 如何实现 Equatable 协议?【英文标题】:How does CLLocation implement the Equatable protocol? 【发布时间】:2017-09-13 22:42:12 【问题描述】:

在回答关于 SO 的另一个问题时,我发现 CLLocation 类符合 Equatable 协议。它用什么方法来判断相等?

纬度/经度的精确匹配?纬度/经度和高度的精确匹配?纬度、经度、高度和时间戳的精确匹配?速度和航向如何? CLLocation 仅使用 lat/long 对创建的对象呢?位置的各种其他值不是可选的,那么使用init(latitude:longitude:) 创建的位置的高度是多少?

【问题讨论】:

通过继承NSObject?如果您尝试使用两个CLLocation 对象调用==,您将看到使用的函数是为NSObject 声明的函数:public func ==(lhs: NSObject, rhs: NSObject) -> Bool。比较两个 CLLocation 实例的“正确”方法可能是使用 distance(from:) 并将其与 CLLocationDistance 阈值进行比较。 如果您想知道使用哪些属性来确定相等性,您应该使用不同的值运行一些测试并查看结果。 【参考方案1】:

只需完全验证日航在他的回答中所说的话,我写道:

import Foundation
import UIKit
import CoreLocation

class ViewController: UIViewController

    var cl1 = CLLocation()
    var cl2 = CLLocation()

    override func viewDidLoad() 
        super.viewDidLoad()
        if cl1 == cl2

        
    

然后我命令点击==(来自if cl1 == cl2)。它带我去:

extension NSObject : CVarArg 


public func ==(lhs: Selector, rhs: Selector) -> Bool

public func ==(lhs: NSObject, rhs: NSObject) -> Bool

public struct NSZone 

为了仔细检查,我点击了CLLocation 并看到了:

open class CLLocation : NSObject, NSCopying, NSSecureCoding 
...

所以基本上== 是因为它是NSObject 的子类,它只比较引用。

【讨论】:

误导性实施。 == 映射到 ===【参考方案2】:

CLLocation 如何实现 Equatable 协议?

它没有。没有覆盖的 == 函数比较两个 CLLocation 实例。当使用两个CLLocation 实例调用== 时,使用NSObject == 函数:

public func ==(lhs: NSObject, rhs: NSObject) -> Bool

要实际比较两个CLLocation 实例,请比较您关心的每个实例的属性(纬度或经度),或者使用内置的distance(from:) 方法与两个位置并将其与CLLocationDistance 阈值进行比较。

【讨论】:

确实,distance(from:) 是比较位置的有用方法,其中== 的默认实现毫无价值。 distance(from:isWithin:) 可让您查看两个位置是否在给定距离内(其中isWithin 以米或公里表示)将是一个很好的扩展。然而,distance(from:) 可能是一个相当昂贵的函数。我怀疑它在内部使用了 Haversine 公式(它可以让您计算 shere 表面上两点之间的“大圆”距离)。 Haversine 公式涉及相当多的三角函数,非常慢。对于大多数应用程序,使用毕达哥拉斯距离进行近似可能会很好,并且速度会快 WHOLE LOT。您甚至可以创建一个版本,使用毕达哥拉斯距离和正弦曲线的粗略近似来非常接近 Haversine 公式,以根据与赤道的距离调整结果。 再说一次,如果您的计算不是时间紧迫的,请不要担心。只需使用distance(from:) 就可以了。您不应该花时间优化实际上不是您正在运行的应用程序性能瓶颈的代码。 (毕竟“过早的优化是万恶之源”。)【参考方案3】:

CLLocation 类很像任何符合 Equatable 的类,实现了 (==) 运算符

为了回答你的其他问题,我决定用这段代码创建一个游乐场

import UIKit
import CoreLocation

var str = "Hello, playground"

var coordinate = CLLocationCoordinate2D.init(latitude: 42.0, longitude: 42.0)
var accuracy = CLLocationAccuracy.init(24.0)
var date = Date.init(timeIntervalSinceNow: 0)

var loc1 = CLLocation.init(coordinate: coordinate, altitude: 44.0, horizontalAccuracy: accuracy, verticalAccuracy: accuracy, timestamp: date)
var loc2 = CLLocation.init(coordinate: coordinate, altitude: 44.0, horizontalAccuracy: accuracy, verticalAccuracy: accuracy, timestamp: date)
var loc3 = CLLocation.init(latitude: 42.0, longitude: 42.0)
var loc4 = CLLocation.init(latitude: 42.0, longitude: 42.0)
var loc5 = CLLocation.init(coordinate: coordinate, altitude: 44.0, horizontalAccuracy: accuracy, verticalAccuracy: accuracy, course: .infinity, speed: 55.0, timestamp: date)
var loc6 = CLLocation.init(coordinate: coordinate, altitude: 44.0, horizontalAccuracy: accuracy, verticalAccuracy: accuracy, course: .infinity, speed: 55.0, timestamp: date)

var bool1 = loc1 == loc2  //false
var bool2 = loc2 == loc3  //false
var bool3 = loc2 == loc2  //true
var bool4 = loc1 == loc4  //false
var bool5 = loc5 == loc6  //false

唯一产生 TRUE 的 bool 是 bool3。

因此,无论不同 CLLocation 对象上的各个属性是否相同,== 运算符都不会将对象视为相等。我猜想比较位置的最佳方法是比较您感兴趣的 CLLocation 对象的字段

【讨论】:

你从未将 loc3 与 loc4 进行过比较! 试一试。但剧透提醒这是错误的 我在 Objective-C 中进行了快速测试,似乎 CLLocation 不会覆盖 isEqual:。两个CLLocation 实例只有在它们是同一个实例时才相等。 这没什么用。看起来 == 实际上是在进行指针比较 (===),因为唯一匹配的比较是将位置与自身进行比较。 如预期的那样

以上是关于CLLocation 如何实现 Equatable 协议?的主要内容,如果未能解决你的问题,请参考以下文章

如何让 Sequence 实现 Equatable?

为自定义私有类实现 Equatable - Swift

如何比较两个实际符合“Equatable”的“Any”对象

如何使用 Equatable 创建扩展以删除自定义数组元素?

Swift 5:在使用协议实现 Equatable 的结构上实现通用数组操作

Swift 3:如何编写 Equatable 函数