Reference as key in swift dictionary

2019-01-24 06:20发布

问题:

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?

回答1:

For Swift 3 (Xcode 8 beta 6 or later), use ObjectIdentifier.

class Test : Hashable {
    // Swift 2:
    var hashValue: Int { return unsafeAddressOf(self).hashValue }
    // Swift 3:
    var hashValue: Int { return ObjectIdentifier(self).hashValue }
}

func ==(lhs: Test, rhs: Test) -> Bool {
    return lhs === rhs
}

Then a == b iff a and b refer to the same instance of the class ( and a.hashValue == b.hashValue is satisfied in that case).

Example:

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

For Swift 2.3 and earlier, you could 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>

to implement a hash value, and the identity operator === to implement the Equatable protocol.



回答2:

Swift 3

This based on the great code snippet in Martin R's answer with insightful comment from Christopher Swasey

class Test: Hashable, Equatable {
    lazy var hashValue: Int = ObjectIdentifier(self).hashValue

    static func ==(lhs: Test, rhs: Test) -> Bool {
        return lhs === rhs
    }
}

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


回答3:

If you don't want or cannot implement Hashable for some reason it's easy to use an Objective C helper:

(long )getPtr:(SomeType* )ptr { return (long )ptr; }

long maps to Swift Int and can be perfectly used as a Swift Dictionary key on both 32 and 64bit architectures. It was the fastest solution which I found while profiling different approaches, including unsafeAddress. In my case performance was the main criteria.