Why do I have to add != to make the comparison correct?
import UIKit
class Person: NSObject {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
extension Person {
static func ==(lhs: Person, rhs: Person) -> Bool {
return lhs.name == rhs.name && lhs.age == rhs.age
}
static func !=(lhs: Person, rhs: Person) -> Bool {
return !(lhs == rhs)
}
}
let first = Person(name: "John", age: 26)
let second = Person(name: "John", age: 26)
/**
* return false (which is correct) when we implement != function. But,
* it will return true if we don't implement the != function.
*/
first != second
Update:
So I got why I had to add != function to make it work. it's because the class inherit the NSObject which uses isEqual method behind the scene. But why does adding != function make it work? Any explanation here?
NSObject conforms to Equatable but uses its own isEqual method and in terms of isEqual both instances are not equal. NSObject calls == only if your form of != is implemented, which contains ==.
If you delete NSObject (and add Equatable) the implementation of == works as expected.
The recommended way for NSObject is to override isEqual with a custom implementation and omit == (and !=).
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
}
}
What you are doing is wrong. You should not implement == or !=. An NSObject subclass automatically implements == as isEqual:. You are disrupting that. You should implement isEqual: and that's all.
Related
class Base: Equatable {
static func == (lhs: Base, rhs: Base) -> Bool {
lhs.id == rhs.id
}
let id: String
init(id: String) {
self.id = id
}
}
class SubClass: Base {
public var id2: String?
public init(id1: String, id2: String? = nil) {
self.id2 = id2
super.init(id: id1)
}
static func == (lhs: SubClass, rhs: SubClass) -> Bool {
lhs.id2 == rhs.id2 && lhs.id == rhs.id
}
}
print(a != b) // result: false
// Calls `Base` class's static func ==
print(a == b) // result: false
// Calls `SubClass` class's static func ==
I have a simple super class and subclass, subclass inherits Base and also implements
static func ==
When calling a != b, it calls Base class's == implementation instead of SubClass's == implementation, why?
But when calling a == b, it actually call's SubClass's == implementation, why?
I expect both != and == calls SubClass's == implementation
This problem existed long time ago since the improvement of Swift language (https://github.com/apple/swift-evolution/blob/master/proposals/0091-improving-operators-in-protocols.md)
And the author also mentioned about this problem. You can check that. https://github.com/apple/swift-evolution/blob/master/proposals/0091-improving-operators-in-protocols.md#class-types-and-inheritance
One of the solution I found is define an isEqual function.
class Superclass: Equatable {
var foo: Int
init(foo: Int) {
self.foo = foo
}
func isEqual(to instance: Superclass) -> Bool {
return self.foo == instance.foo
}
static func ==(lhs: Superclass, rhs: Superclass) -> Bool {
return type(of: lhs) == type(of: rhs) && lhs.isEqual(to: rhs)
}
}
class Subclass: Superclass {
var bar: String
init(foo: Int, bar: String) {
self.bar = bar
super.init(foo: foo)
}
override func isEqual(to instance: Superclass) -> Bool {
guard let instance = instance as? Subclass else {
return false
}
return self.foo == instance.foo && self.bar == instance.bar
}
}
let a = Subclass(foo: 1, bar: "a")
let b = Subclass(foo: 1, bar: "b")
print(a == b) // False
print(a != b) // True
Reference:
https://forums.swift.org/t/method-dispatching-issue-with-subclasses-implementing-equatable-protocol/4925
https://forums.swift.org/t/implement-equatable-protocol-in-a-class-hierarchy/13844
When subclassing NSObject in Swift, should you override hash or implement Hashable?
Also, should you override isEqual: or implement the == operator?
NSObject already conforms to the Hashable protocol:
extension NSObject : Equatable, Hashable {
/// The hash value.
///
/// **Axiom:** `x == y` implies `x.hashValue == y.hashValue`
///
/// - Note: the hash value is not guaranteed to be stable across
/// different invocations of the same program. Do not persist the
/// hash value across program runs.
public var hashValue: Int { get }
}
public func ==(lhs: NSObject, rhs: NSObject) -> Bool
I could not find an official reference, but it seems that hashValue
calls the hash method from NSObjectProtocol, and == calls the
isEqual: method (from the same protocol). See update at the
end of the answer!
For NSObject subclasses, the correct way seems to be
to override hash and isEqual:, and here is an experiment which
demonstrates that:
1. Override hashValue and ==
class ClassA : NSObject {
let value : Int
init(value : Int) {
self.value = value
super.init()
}
override var hashValue : Int {
return value
}
}
func ==(lhs: ClassA, rhs: ClassA) -> Bool {
return lhs.value == rhs.value
}
Now create two different instances of the class which are considered
"equal" and put them into a set:
let a1 = ClassA(value: 13)
let a2 = ClassA(value: 13)
let nsSetA = NSSet(objects: a1, a2)
let swSetA = Set([a1, a2])
print(nsSetA.count) // 2
print(swSetA.count) // 2
As you can see, both NSSet and Set treat the objects as different.
This is not the desired result. Arrays have unexpected results as well:
let nsArrayA = NSArray(object: a1)
let swArrayA = [a1]
print(nsArrayA.indexOfObject(a2)) // 9223372036854775807 == NSNotFound
print(swArrayA.indexOf(a2)) // nil
Setting breakpoints or adding debug output reveals that the overridden
== operator is never called. I don't know if this is a bug or
intended behavior.
2. Override hash and isEqual:
class ClassB : NSObject {
let value : Int
init(value : Int) {
self.value = value
super.init()
}
override var hash : Int {
return value
}
override func isEqual(object: AnyObject?) -> Bool {
if let other = object as? ClassB {
return self.value == other.value
} else {
return false
}
}
}
For Swift 3, the definition of isEqual: changed to
override func isEqual(_ object: Any?) -> Bool { ... }
Now all results are as expected:
let b1 = ClassB(value: 13)
let b2 = ClassB(value: 13)
let nsSetB = NSSet(objects: b1, b2)
let swSetB = Set([b1, b2])
print(swSetB.count) // 1
print(nsSetB.count) // 1
let nsArrayB = NSArray(object: b1)
let swArrayB = [b1]
print(nsArrayB.indexOfObject(b2)) // 0
print(swArrayB.indexOf(b2)) // Optional(0)
Update: The behavior is documented in the book "Using Swift with Cocoa and Objective-C", under "Interacting with Objective-C API":
The default implementation of the == operator invokes the isEqual: method, and the default implementation of the === operator checks pointer equality. You should not override the equality or identity operators for types imported from Objective-C.
The base implementation of the isEqual: provided by the NSObject class is equivalent to an identity check by pointer equality. You can override isEqual: in a subclass to have Swift and Objective-C APIs determine equality based on the contents of objects rather than their identities.
The book is available in the Apple Book app.
It was also documented on Apple's website but was removed, and is still visible on the WebArchive snapshot of the page.
For NSObject it is best to override hash and isEqual. It already conforms to Hashable and Equatable and has synthesized conformances for that which in turn invoke hash and isEqual. So since it is an NSObject, do it the ObjC way and override the values that also affect the ObjC hash value and equality.
class Identity: NSObject {
let name: String
let email: String
init(name: String, email: String) {
self.name = name
self.email = email
}
override var hash: Int {
var hasher = Hasher()
hasher.combine(name)
hasher.combine(email)
return hasher.finalize()
}
override func isEqual(_ object: Any?) -> Bool {
guard let other = object as? Identity else {
return false
}
return name == other.name && email == other.email
}
}
"Also, should you override isEqual: or implement ==?"
You could do both. And in case you'd make the implementations
behave differently you'd add flare to the life of the users of your code too. Been there, done that. It's fun.
Implement Hashable, which also requires you to implement the == operator for your type. These are used for a lot of useful stuff in the Swift standard library like the indexOf function which only works on collections of a type that implements Equatable, or the Set<T> type which only works with types that implement Hashable.
I'm trying to implement the == operator (from Equatable) in a base class and its subclasses in Swift 3. All of the classes will only be used in Swift so I do not want to involve NSObject or the NSCopying protocol.
I started with a base class and a subclass:
class Base {
var x : Int
}
class Subclass : Base {
var y : String
}
Now I wanted to add Equatable and the == operator to Base. Seems simple enough. Copy the == operator signature from the documentation:
class Base : Equatable {
var x : Int
static func == (lhs: Base, rhs: Base) -> Bool {
return lhs.x == rhs.x
}
}
So far so good. Now for the subclass:
class Subclass : Base {
static override func == (lhs: Base, rhs: Base) -> Bool {
return true
}
}
But this results in an error:
Operator function overrides a 'final' operator function
OK. After some research (still learning Swift 3) I learn that static can be replaced with class to indicate the type method can be overridden.
So I attempt to change static to class in Base:
class Base : Equatable {
var x : Int
class func == (lhs: Base, rhs: Base) -> Bool {
return lhs.x == rhs.x
}
}
But that results in a new error:
Operator '==' declared in non-final class 'Base' must be 'final'
Ugh. This is far more complicated than it should be.
How do I implement the Equatable protocol and the == operator properly in a base class and a subclass?
After lots of research and some trial and error I finally came up with a working solution. The first step was moving the == operator from inside the class to the global scope. This fixed the errors about static and final.
For the base class this became:
func == (lhs: Base, rhs: Base) -> Bool {
return lhs.x == rhs.x
}
class Base : Equatable {
var x : Int
}
And for the subclass:
func == (lhs: Subclass, rhs: Subclass) -> Bool {
return true
}
class Subclass : Base {
var y : String
}
Now the only part left is figuring out how to call the == operator of the base class from the == operator of the subclass. This led me to the final solution:
func == (lhs: Subclass, rhs: Subclass) -> Bool {
if lhs.y == rhs.y {
if lhs as Base == rhs as Base {
return true
}
}
return false
}
That first if statement results in a call to the == operator in the base class.
The final solution:
Base.swift:
func == (lhs: Base, rhs: Base) -> Bool {
return lhs.x == rhs.x
}
class Base : Equatable {
var x : Int
}
Subclass.swift:
func == (lhs: Subclass, rhs: Subclass) -> Bool {
if lhs.y == rhs.y {
if lhs as Base == rhs as Base {
return true
}
}
return false
}
class Subclass : Base {
var y : String
}
Following the other answers I came up with this:
class Base : Equatable {
var x : Int
static func == (lhs: Base, rhs: Base) -> Bool {
return lhs.x == rhs.x
}
}
class Subclass : Base {
var y : String
static func == (lhs: Subclass, rhs: Subclass) -> Bool {
return lhs.y == rhs.y && (lhs as Base) == (rhs as Base)
}
}
I know it's been a while since the question is posted, but I hope my answer helps.
TLDR -- Instead of trying to override ==, you provide a custom comparing method, make == call it, and override the custom comparing method if needed.
So you said
All of the classes will only be used in Swift so I do not want to involve NSObject or the NSCopying protocol.
But if you were to subclass NSObject, how will you write your custom comparison method? You will override isEqual(Any?), right? And if you try to conform to Equatable protocol in your subclass, compiler will complain about "Redundant conformance to protocol Equatable" because NSObject already conformed to Equatable.
Now that gives us some hints about how NSObject handles this problem -- it provides a custom comparing method isEqual(Any?), call it inside ==, and its subclasses can override it if needed. You can do the same in your own base class.
Without further ado, let's do some experiments(in Swift 4). Define some classes
class Grandpa: Equatable {
var x = 0
static func ==(lhs: Grandpa, rhs: Grandpa) -> Bool {
return lhs.isEqual(to: rhs)
}
func isEqual(to object: Any?) -> Bool {
guard object != nil && type(of: object!) == Grandpa.self else {
return false
}
let value = object as! Grandpa
return x == value.x
}
}
class Father: Grandpa {
var y = 0
override func isEqual(to object: Any?) -> Bool {
guard object != nil && type(of: object!) == Father.self else {
return false
}
let value = object as! Father
return x == value.x && y == value.y
}
}
class Son: Father {
var z = 0
override func isEqual(to object: Any?) -> Bool {
guard object != nil && type(of: object!) == Son.self else {
return false
}
let value = object as! Son
return x == value.x && y == value.y && z == value.z
}
}
And write some test code
let grandpa1 = Grandpa()
let grandpa2 = Grandpa()
let grandpa3: Grandpa? = nil
let grandpa4: Grandpa? = nil
let father1 = Father()
let father2 = Father()
let father3 = Father()
father3.y = 1
let son1 = Son()
let son2 = Son()
let son3 = Son()
son3.z = 1
print("grandpa1 == grandpa2: \(grandpa1 == grandpa2)")
print("grandpa1 == grandpa3: \(grandpa1 == grandpa3)")
print("grandpa3 == grandpa4: \(grandpa3 == grandpa4)")
print("grandpa1 == father1: \(grandpa1 == father1)")
print("father1 == father2: \(father1 == father2)")
print("father1 == father3: \(father1 == father3)")
print("son1 == son2: \(son1 == son2)")
print("son1 == son3: \(son1 == son3)")
Run it and you should get
grandpa1 == grandpa2: true
grandpa1 == grandpa3: false
grandpa3 == grandpa4: true
grandpa1 == father1: false
father1 == father2: true
father1 == father3: false
son1 == son2: true
son1 == son3: false
Following rmaddy's answer, I came up with a guard approach for testing equality:
Base.swift
static func ==(lhs: Base, rhs: Base) -> Bool {
// ensure class properties match
guard lhs.x == rhs.x else {
return false
}
return true
}
Subclass.swift
static func ==(lhs: Subclass, rhs: Subclass) -> Bool {
// ensure base class properties match
guard lhs as Base == rhs as Base else {
return false
}
// ensure class properties match
guard lhs.y == rhs.y else {
return false
}
return true
}
```
I am getting frustrated with how Swift handles equality. Or I'm just missing something. How come these 2 indexOf DOESN'T work the same way?
let first = self.objects.indexOf(object) //This returns nil
let second = self.objects.indexOf{$0 == object} //This returns the index
My == override:
func ==(lhs: MyObject, rhs: MyObject) -> Bool {
return lhs.someProperty == rhs.someProperty
}
The == override doesn't get called in the first indexOf. Why is that so? This feels really dangerous.
(MyObject is a subclass of PFObject (Parse.com objects). I don't know if this is what's messing this up.)
Since your Object is subclass of PFObject, and PFObject is subclass of NSObject, and NSObject already confirm to Equatable by using the isEqual method to implement. So your == operator override is not working. You should override the isEqual method. (To be honest, it's horrible :(
sample code:
class Object: NSObject {
var value: Int
init(value: Int) {
self.value = value
}
override func isEqual(object: AnyObject?) -> Bool {
guard let obj = object as? Object else { return false }
return self.value == obj.value
}
}
// If your class is not inherit from any ObjC class:
//extension Object: Equatable {}
//func ==(lhs: Object, rhs: Object) -> Bool {
// return lhs.value == rhs.value
//}
let c = Object(value: 3)
let objs = [Object(value: 1), Object(value: 2), Object(value: 3)]
let index = objs.indexOf(c)
let index2 = objs.indexOf { $0 == c }
print(index) // Optional(2)
print(index2) // Optional(2)
The second indexOf method that you are using allows you to define a closure to indicate object equality (like you have already done). The first one according to the documentation:
Returns the first index where value appears in self or nil if value is not found.
Where value is the object you are passing as the parameter. Without seeing the implementation of this version I can only assume they compare if the two objects are equal in every way (all properties match). So in your case, if you only consider the objects equal if the somePropertys match, then you should be using the closure method with your overloaded operator.
static func == (left: MyClass, right: MyClass) -> Bool {
return left.attribute1 == right.attribute1
}
override func isEqual(_ object: Any?) -> Bool {
guard let right = object as? MyClass else {
return false
}
// call the == from above
// why couldn't apple do this ???
//
return self == right
}
When subclassing NSObject in Swift, should you override hash or implement Hashable?
Also, should you override isEqual: or implement the == operator?
NSObject already conforms to the Hashable protocol:
extension NSObject : Equatable, Hashable {
/// The hash value.
///
/// **Axiom:** `x == y` implies `x.hashValue == y.hashValue`
///
/// - Note: the hash value is not guaranteed to be stable across
/// different invocations of the same program. Do not persist the
/// hash value across program runs.
public var hashValue: Int { get }
}
public func ==(lhs: NSObject, rhs: NSObject) -> Bool
I could not find an official reference, but it seems that hashValue
calls the hash method from NSObjectProtocol, and == calls the
isEqual: method (from the same protocol). See update at the
end of the answer!
For NSObject subclasses, the correct way seems to be
to override hash and isEqual:, and here is an experiment which
demonstrates that:
1. Override hashValue and ==
class ClassA : NSObject {
let value : Int
init(value : Int) {
self.value = value
super.init()
}
override var hashValue : Int {
return value
}
}
func ==(lhs: ClassA, rhs: ClassA) -> Bool {
return lhs.value == rhs.value
}
Now create two different instances of the class which are considered
"equal" and put them into a set:
let a1 = ClassA(value: 13)
let a2 = ClassA(value: 13)
let nsSetA = NSSet(objects: a1, a2)
let swSetA = Set([a1, a2])
print(nsSetA.count) // 2
print(swSetA.count) // 2
As you can see, both NSSet and Set treat the objects as different.
This is not the desired result. Arrays have unexpected results as well:
let nsArrayA = NSArray(object: a1)
let swArrayA = [a1]
print(nsArrayA.indexOfObject(a2)) // 9223372036854775807 == NSNotFound
print(swArrayA.indexOf(a2)) // nil
Setting breakpoints or adding debug output reveals that the overridden
== operator is never called. I don't know if this is a bug or
intended behavior.
2. Override hash and isEqual:
class ClassB : NSObject {
let value : Int
init(value : Int) {
self.value = value
super.init()
}
override var hash : Int {
return value
}
override func isEqual(object: AnyObject?) -> Bool {
if let other = object as? ClassB {
return self.value == other.value
} else {
return false
}
}
}
For Swift 3, the definition of isEqual: changed to
override func isEqual(_ object: Any?) -> Bool { ... }
Now all results are as expected:
let b1 = ClassB(value: 13)
let b2 = ClassB(value: 13)
let nsSetB = NSSet(objects: b1, b2)
let swSetB = Set([b1, b2])
print(swSetB.count) // 1
print(nsSetB.count) // 1
let nsArrayB = NSArray(object: b1)
let swArrayB = [b1]
print(nsArrayB.indexOfObject(b2)) // 0
print(swArrayB.indexOf(b2)) // Optional(0)
Update: The behavior is documented in the book "Using Swift with Cocoa and Objective-C", under "Interacting with Objective-C API":
The default implementation of the == operator invokes the isEqual: method, and the default implementation of the === operator checks pointer equality. You should not override the equality or identity operators for types imported from Objective-C.
The base implementation of the isEqual: provided by the NSObject class is equivalent to an identity check by pointer equality. You can override isEqual: in a subclass to have Swift and Objective-C APIs determine equality based on the contents of objects rather than their identities.
The book is available in the Apple Book app.
It was also documented on Apple's website but was removed, and is still visible on the WebArchive snapshot of the page.
For NSObject it is best to override hash and isEqual. It already conforms to Hashable and Equatable and has synthesized conformances for that which in turn invoke hash and isEqual. So since it is an NSObject, do it the ObjC way and override the values that also affect the ObjC hash value and equality.
class Identity: NSObject {
let name: String
let email: String
init(name: String, email: String) {
self.name = name
self.email = email
}
override var hash: Int {
var hasher = Hasher()
hasher.combine(name)
hasher.combine(email)
return hasher.finalize()
}
override func isEqual(_ object: Any?) -> Bool {
guard let other = object as? Identity else {
return false
}
return name == other.name && email == other.email
}
}
"Also, should you override isEqual: or implement ==?"
You could do both. And in case you'd make the implementations
behave differently you'd add flare to the life of the users of your code too. Been there, done that. It's fun.
Implement Hashable, which also requires you to implement the == operator for your type. These are used for a lot of useful stuff in the Swift standard library like the indexOf function which only works on collections of a type that implements Equatable, or the Set<T> type which only works with types that implement Hashable.