Cocoa defines its own hashing API that is independent of Swift's Hashable
. To customize hashing in NSObject
subclasses, the correct property to override is NSObject.hash
.
Trying to customize hashing by overriding hash(into:)
and/or hashValue
will not work correctly -- if you do that then NSDictionary
, NSSet
, and Foundation's other hashing collections won't work correctly with your class, often leading to severe problems.
It's rather unfortunate that hash
and hashValue
have distinct names; they are all too easy to confuse. In earlier Swift versions, NSObject.hashValue
was accidentally left overridable, which added to the confusion.
Existing code that overrides NSObject.hashValue
has been broken from the start; it needs to be fixed. To help us fix existing code and to prevent further problems, Swift 4.2 emits a deprecation warning for NSObject.hashValue
overrides, and defines NSObject.hash(into:)
non-overridable.
This doesn't mean that you can't use Hasher
for such subclasses. For example, here is the proper way to define HashClass
with custom hashing:
import Foundation
public final class HashClass: NSObject {
var i: Int = 0
public override func isEqual(_ other: Any) -> Bool {
guard let other = other as? HashClass else { return false }
return self.i == other.i
}
public override var hash: Int {
var hasher = Hasher()
hasher.combine(i)
return hasher.finalize()
}
}
Important note: If you override NSObject.hash
, you must also override isEqual(_:)
. This isn't currently enforced, but forgetting to do so is another source of subtle but deadly bugs.
Any isEqual(_:)
override you provide will get used by both Swift's == operator and Objective-C code that works with NSObjects.