5

In answering another question on SO, I found that the CLLocation class conforms to the Equatable protocol. What method does it use to determine equality?

Exact match of lat/long? Exact match of lat/long and altitude? Exact match of latitude, longitude, altitude, and timestamp? What about speed and course? What about CLLocation objects that were created with only a lat/long pair? Various other values of the location are not optionals, so what would the altitude be for a location created using init(latitude:longitude:)?

JAL
  • 41,701
  • 23
  • 172
  • 300
Duncan C
  • 128,072
  • 22
  • 173
  • 272
  • By inheriting from `NSObject`? If you try to call `==` with two `CLLocation` objects, you will see that the function used is the one declared for `NSObject`: `public func ==(lhs: NSObject, rhs: NSObject) -> Bool`. The "correct" way to compare two `CLLocation` instances would probably be to use `distance(from:)` and compare that to a `CLLocationDistance` threshold. – JAL Sep 13 '17 at 22:51
  • 1
    If you want to know what properties are used to determine equality you should run a few tests with various values and see what the results are. – rmaddy Sep 13 '17 at 23:09

3 Answers3

2

How does CLLocation implement the Equatable protocol?

It doesn't. There is no overridden == function which compares two CLLocation instances. When calling == with two CLLocation instances, the NSObject == function is used:

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

To actually compare two CLLocation instances, either compare the properties on each you care about (latitude or longitude), or use the built in distance(from:) method with two locations and compare that to a CLLocationDistance threshold.

JAL
  • 41,701
  • 23
  • 172
  • 300
  • Indeed, `distance(from:)` would a useful way to compare locations, where the default implementation of `==` is worthless. A `distance(from:isWithin:)` that let you see if two location were within a given distance of each other (where `isWithin` is expressed in meters or kilometers) would be a good extension. However, `distance(from:)` is likely a pretty expensive function. I suspect it uses the Haversine formula internally (which lets you calculate the "great circle" distance between two points on the surface of a shere). – Duncan C Mar 21 '20 at 23:52
  • The Haversine formula involves quite a bit of trig, which is very slow. For most applications, doing an approximation using Pythagorean distance would likely be fine, and a **WHOLE LOT** faster. You could even create a version that gave a very close approximation of the Haversine formula using Pythagorean distance and a crude approximation of a sine curve to adjust the result based on distance from the equator. – Duncan C Mar 21 '20 at 23:53
  • Then again, if your calculations are not time critical, don't worry about it. Just use `distance(from:)` and be done with it. You sholdn't spend time optimizing code that isn't actually a performance bottleneck to your running app. ("Premature optimization is the root of all evil" after all.) – Duncan C Mar 22 '20 at 00:03
2

Just fully verify what JAL has said in his answer, I wrote:

import Foundation
import UIKit
import CoreLocation

class ViewController: UIViewController{

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

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

        }
    }
}

Then I command clicked on the == (from if cl1 == cl2). It took me to:

extension NSObject : CVarArg {
}

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

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

public struct NSZone {
}

To double check I command clicked on CLLocation and saw:

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

So basically the == is because it's subclassed from NSObject which only compares references.

mfaani
  • 33,269
  • 19
  • 164
  • 293
0

CLLocation class much like any class that conforms to Equatable, implements the (==) operator

And to answer your other questions I decided to start up a playground with this code

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

and the only bool that yields TRUE is bool3.

so regardless if the individual properties on different CLLocation objects are the same the == operator will not see the objects as equal. Im guessing the best way to for one to compare locations is to compare the fields off the CLLocation objects that are of interest to you

sfbarry14
  • 61
  • 5
  • 1
    You never compared loc3 to loc4! – Duncan C Sep 13 '17 at 23:29
  • take it for a spin. but spoiler alert it's false – sfbarry14 Sep 13 '17 at 23:30
  • I ran a quick test in Objective-C and it appears that `CLLocation` does not override `isEqual:`. Two `CLLocation` instances are only equal if they are the same instance. – rmaddy Sep 13 '17 at 23:33
  • That's pretty useless. Looks like `==` is actually doing pointer comparison (`===`) since the only comparison that matches is comparing a location to itself. – Duncan C Sep 13 '17 at 23:33
  • "CLLocation class much like any class that conforms to Equatable, implements the (==) operator". Uh, yeah, that's axiomatic. What I meant was how does it do the comparison algorithmically? – Duncan C Sep 13 '17 at 23:34
  • `var loc7 = loc3.copy() as! CLLocation`; `var bool7 = loc7 == loc3` //This code gives false! – Duncan C Sep 13 '17 at 23:38
  • 2
    that is as expected – sfbarry14 Sep 13 '17 at 23:40
  • and most likely var loc8 = lov7; var bool8 = loc8 == lov7; is true – sfbarry14 Sep 13 '17 at 23:43
  • I smell an opportunity for an extension to `CLLocation` that compares just the properties you care about in your project. :) – rmaddy Sep 13 '17 at 23:52
  • Like I said in my comment, this is only true because of the inheritance from `NSObject`. The `CLLocation` class itself does not implement `==`. – JAL Sep 14 '17 at 00:46
  • Your statement `CLLocation class much like any class that conforms to Equatable, implements the (==) operator` is wrong. – JAL Sep 14 '17 at 00:56