6

I've come across a strange issue related to Swift/Objective-C interoperability. The case is this:

I have an @objc public class GKDistance:NSObject,NSCoding,Comparable written in Swift. In order to compare distances, I have added an operator overload for < as follows:

public func <(a:GKDistance, b:GKDistance) -> Bool {
    return a.value < b.value
}

public func ==(a:GKDistance, b:GKDistance) -> Bool {
    return a.value == b.value
}

It's then used in an Objective-C method like so:

if (distance < averageDistance){
    // code
}

When the Objective-C method is called I can add a print() statement and breakpoint in the < method to confirm when the operator overload is being used. In one case, it mysteriously skips the operator overload defined in Swift, and uses the regular Objective-C comparator between two GKDistance objects.

When run with distance.value == 2375.1842554877021 and averageDistance.value == 75.671794891357421, distance < averageDistance returns true, the Swift operator overload is never hit, and Objective-C executes the code inside the conditional.

If I convert the Objective-C method in question to Swift, it behaves as expected, but I'm concerned that there are other GKDistance comparisons in various Objective-C methods throughout our app that could be failing to see the Swift operator overload.

Has anyone run into similar issues with Swift/Objective-C interoperability as it pertains to operator overloading?

hellerahum
  • 103
  • 6

1 Answers1

4

Your

public func <(a:GKDistance, b:GKDistance) -> Bool { }

is – like all Swift operators – a top-level function, and top-level functions are not exported to Objective-C, see "Swift Type Compatibility" in "Interacting with Objective-C APIs".

Also note that you cannot override operators (like <) in (Objective-)C, see e.g.

Therefore in

if (distance < averageDistance) {
    // code
}

just the two pointers are compared, and the condition is true if the object pointed to by distance resides in a lower memory address than the object pointer to by averageDistance (which is obviously not what you intended).

What you could do is to implement a compare: method in your class, similar to the one of NSString or NSNumber.

Note also that for NSObject subclasses, you should override isEqual: instead of implementing ==, compare

Community
  • 1
  • 1
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • 1
    I don't think you have answered the question. While I understand that you cannot overload operators (like <) in Objective-C, the question is ... when you overload an operator in Swift, then call that function from the ObjC to Swift bridge can Objective C use the (very legal) operator overload in Swift. Also, can you point to any documentation justifying your position? – Colin Phillips Jun 07 '16 at 14:59
  • 1
    @ColinPhillips: `if (distance < averageDistance)` does not call any function, and the `<` operator is "built-in" in C, compare e.g. http://stackoverflow.com/questions/3417413/operator-overloading-in-c. That are two pointers, and they are compared. – Martin R Jun 07 '16 at 15:01
  • 1
    @ColinPhillips: I have added some references. – Martin R Jun 07 '16 at 15:09
  • This is the correct answer. Operator overloads specified in Swift classes will NOT be accessible to Objective-C, and the Objective-C code will compare pointers 100% of the time. – hellerahum Jun 07 '16 at 16:01
  • Thanks for the clarification @MartinR - I have up-voted your answer. – Colin Phillips Jun 07 '16 at 17:04