问题
I've started to learn swift after Java. In Java I can use any object as a key for HashSet, cause it has default hashCode and equals based on object identifier. How to achieve the same behaviour in Swift?
回答1:
If you are working with classes and not structs, you can use the ObjectIdentifier struct. Note that you also have to define == for your class in order to conform to Equatable (Hashable requires it). It would look something like this:
class MyClass: Hashable { }
func ==(lhs: MyClass, rhs: MyClass) -> Bool {
return ObjectIdentifier(lhs) == ObjectIdentifier(rhs)
}
class MyClass: Hashable {
var hashValue: Int {
return ObjectIdentifier(self).hashValue
}
}
回答2:
In Swift, the type must conform to Hashable and Equatable for it to be used in a data structure such as a Dictionary or a Set. However, you can add "automatic conformance" by using the "object identifier" of the object. In the code below, I implemented a reusable class to do this automatically.
Note, Swift 4.2 changed how Hashable is implemented, so you no longer override hashValue. Instead, you override hash(into:).
open class HashableClass {
public init() {}
}
// MARK: - <Hashable>
extension HashableClass: Hashable {
public func hash(into hasher: inout Hasher) {
hasher.combine(ObjectIdentifier(self).hashValue)
}
// `hashValue` is deprecated starting Swift 4.2, but if you use
// earlier versions, then just override `hashValue`.
//
// public var hashValue: Int {
// return ObjectIdentifier(self).hashValue
// }
}
// MARK: - <Equatable>
extension HashableClass: Equatable {
public static func ==(lhs: HashableClass, rhs: HashableClass) -> Bool {
return ObjectIdentifier(lhs) == ObjectIdentifier(rhs)
}
}
To use, just take your class and subclass HashableClass, then everything should just work!
class MyClass: HashableClass {
}
回答3:
Another option is to implement an extension to Hashable and Equatable protocols for AnyObject. It seems to achieve a similar effect as the one you mentioned in Java.
It adds a default behavior to all classes within your project and is not necessary the better option compared to adding such behavior to only a designated class. So, I am just mentioning it for the sake of completeness:
class HashableClass: Hashable {
}
extension Hashable where Self: AnyObject{
func hash(into hasher: inout Hasher) {
hasher.combine(ObjectIdentifier(self))
}
}
extension Equatable where Self: AnyObject{
static func ==(lhs: Self, rhs: Self) -> Bool {
return lhs === rhs
}
}
Now, if you you would like to implement specific logic for your class, you could still do that, like this:
class HashableClass { //deleted the Hashable conformance
}
extension HashableClass : Hashable{
func hash(into hasher: inout Hasher) {
//your custom hashing logic
}
}
To avoid adding default behavior to AnyObject, another protocol can be declared and extension relevant only to this new protocol can be added:
protocol HashableClass : AnyObject{
}
class SomeClass: Hashable, HashableClass {
}
extension Hashable where Self: HashableClass{
func hash(into hasher: inout Hasher) {
hasher.combine(ObjectIdentifier(self))
}
}
extension Equatable where Self: HashableClass{
static func ==(lhs: Self, rhs: Self) -> Bool {
return lhs === rhs
}
}
来源:https://stackoverflow.com/questions/34705786/swift-how-to-implement-hashable-protocol-based-on-object-reference