Swift, how to implement Hashable protocol based on object reference?

烈酒焚心 提交于 2019-12-04 16:07:38

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!