Sorry, this is not a direct answer to your question.
As Alexander commented, Swift Standard Library has this default implementation of !=:
Equatable.swift
@_transparent
public static func != (lhs: Self, rhs: Self) -> Bool {
return !(lhs == rhs)
}
I cannot explain this behavior well, but the == operator in the default implementation above is solved to the default == operator for NSObject, as NSObject (and also its descendants) is already Equatable and has an == operator to conform to Equatable. So, even if the explicit representation is exactly the same as your != definition, the == operators are solved to different implementations.
A general guidline to define your own equality to an NSObject-descendant class:
Make == and isEqual(_:) consistent
You may store your class's instance inside NSArray or NSDictionary (in many cases implicitly). Inside their methods, isEqual(_:) is used when equality check is needed, not the == operator.
So, just defining the == operator without giving a consistent override to isEqual(_:), such methods will generate unexpected result.
To make consistent == and isEqual(_:),
just override only isEqual(_:) and do not define == and != explicitly.
The default implementation of == for NSObject (and also !=) uses isEqual(_:).
class Person: NSObject {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
override func isEqual(_ object: Any?) -> Bool {
if let other = object as? Person {
return self.name == other.name && self.age == other.age
}
return false
}
}
(See ONE MORE THING at the bottom.)
ADDITION
Similar behavior can be found on non-NSObject classes.
class BaseClass {
var a: Int
init(a: Int) {
self.a = a
}
}
extension BaseClass: Equatable {
static func == (lhs: BaseClass, rhs: BaseClass) -> Bool {
print("`==` of BaseClass")
return lhs.a == rhs.a
}
}
let b1 = BaseClass(a: 0)
let b2 = BaseClass(a: 0)
print(b1 != b2) //->`==` of BaseClass, false ### as expected
class DerivedClass: BaseClass {
var b: Int
init(a: Int, b: Int) {
self.b = b
super.init(a: a)
}
}
extension DerivedClass {
static func == (lhs: DerivedClass, rhs: DerivedClass) -> Bool {
print("`==` of DerivedClass")
return lhs.a == rhs.a && lhs.b == rhs.b
}
}
let d1 = DerivedClass(a: 0, b: 1)
let d2 = DerivedClass(a: 0, b: 2)
print(d1 != d2) //->`==` of BaseClass, false ### `==` of DerivedClass and true expected
Seems we need extra care when overriding == for already Equatable classes.
ONE MORE THING
(Thanks for Hamish.)
You know you need to implement == and hashValue consistently when creating a type conforming to Hashable. NSObject is declared as Hashable, and its hashValue needs to be consistent with hash. So, when you override isEqual(_:) in your NSObject-descendent, you also should override hash consistent with your overridden isEqual(_:).
So, your Person class should be something like this:
class Person: NSObject {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
override func isEqual(_ object: Any?) -> Bool {
if let other = object as? Person {
return self.name == other.name && self.age == other.age
}
return false
}
override var hash: Int {
//### This is just an example, but not too bad in practical use cases.
return name.hashValue ^ age.hashValue
}
}