Equality in Swift: NSObject, Subclasses, and Existentials
Conformance to the
Equatable
protocol seems pretty straightforward. You simply override the==
function.[…]
This works great for objects like structs, or classes with no superclass. However, we can run into problems with the
==
function if we’re dealing withNSObject
subclasses.
Jayesh Kawli (via Marcin Krzyzanowski):
And now if you try to do following comparison, they will either won’t work, will be buggy or fail to compile
Swift can be tricky sometimes. For example, what does the following print?
class A: NSObject { let x: Int init(x: Int) { self.x = x } } func ==(left: A, right: A) -> Bool { return left.x == right.x } print(A(x: 1) == A(x: 1)) print([A(x: 1)] == [A(x: 1)])[…]
The best way to make an
NSObject
subclass use custom equality inside an array is to overrideisEqual:
[…]
His reasoning for why the custom ==
didn’t work as expected is wrong, but the solution is correct. Similarly, you should override hashValue
(not hash(into:)
) if you need to change how it is Hashable
.
For non-NSObject
classes, a similar issue applies. If you have something like:
class Base: Equatable { static func == (lhs: Base, rhs: Base) -> Bool { return lhs === rhs } } class A: Base { let x: Int init(x: Int) { self.x = x } } func ==(left: A, right: A) -> Bool { return left.x == right.x }
The results may not be what you expect:
A(x: 1) == A(x: 1) // true [A(x: 1)] == [A(x: 1)] // false
Array
uses the ==
from where its elements conformed to Equatable
.
In Swift 5.7 that comes with Xcode 14 we can more easily check if two values of type
Any
are equal, because we can cast values toany Equatable
and also useany Equatable
as a parameter type thanks to Unlock existentials for all protocols change.[…]
Inside
isEqual()
method we have access toSelf
, so we can try to cast theother
value to the same type as the concrete type of the value this method is called on. If the cast fails, the two values cannot be equal, so we returnfalse
. If it succeeds, then we can check the two values for equality, using==
function on values of the same concrete type.
There are some edge cases to be aware of, however, so it is preferred to use AnyHashable
.
You can define something similar for
Comparable
[…]
Previously:
- Swift 5.7
- Dynamic Equality Checking and Equatable
- Some Swift Types Are More Equatable Than Others
- Swift Protocols
Update (2023-08-09): See also: Helge Heß.