Search code examples
swiftdictionarykeyhashtablememory-address

Reference as key in swift dictionary


Dictionary key requires Hashable conformance:

class Test {}
var dictionary = [Test: String]() // Type 'Test' dies not conform to protocol 'Hashable'

class Test: NSObject {}
var dictionary = [Test: String]() // Works

How to get address of pure Swift class instance to use as hashValue?


Solution

  • Equality can be implemented as object identity, i.e. a == b iff a and b refer to the same instance of the class, and the hash value can be build from the ObjectIdentifier (which is the same for identical objects, compare e.g. Difference between using ObjectIdentifier() and '===' Operator):

    For Swift 4.2 and later:

    class Test : Hashable {
        static func ==(lhs: Test, rhs: Test) -> Bool {
            return lhs === rhs
        }
    
        public func hash(into hasher: inout Hasher) {
            hasher.combine(ObjectIdentifier(self))
        }
    }
    

    For Swift 3:

    class Test : Hashable {
        var hashValue: Int { return ObjectIdentifier(self).hashValue }
    }
    
    func ==(lhs: Test, rhs: Test) -> Bool {
        return lhs === rhs
    }
    

    For Swift 2.3 and earlier, you can use

    /// Return an UnsafePointer to the storage used for `object`.  There's
    /// not much you can do with this other than use it to identify the
    /// object
    func unsafeAddressOf(object: AnyObject) -> UnsafePointer<Void>
    

    i.e.

    class Test : Hashable {
        var hashValue: Int { return unsafeAddressOf(self).hashValue }
    }
    
    func ==(lhs: Test, rhs: Test) -> Bool {
        return lhs === rhs
    }
    

    Example:

    var dictionary = [Test: String]()
    let a = Test()
    let b = Test()
    dictionary[a] = "A"
    print(dictionary[a]) // Optional("A")
    print(dictionary[b]) // nil
    

    implement the Equatable protocol.