Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 822
Kirsteins Avatar asked May 20 '15 09:05

Kirsteins


People also ask

Are dictionaries passed by reference Swift?

Learn to build a dictionary in Swift passed by referenceDictionaries in Swift are passed by value. It means that if a dictionary is passed, or copied, a second instance is created.

Is it possible to use a custom struct as a dictionary key Swift?

Any type that conforms to the Hashable protocol can be used as a dictionary's Key type, including all of Swift's basic types. You can use your own custom types as dictionary keys by making them conform to the Hashable protocol.

What is key in dictionary Swift?

Swift dictionary is an unordered collection of items. It stores elements in key/value pairs. Here, keys are unique identifiers that are associated with each value.

How do you check if a dictionary contains a key in Swift?

Swift – Check if Specific Key is Present in Dictionary To check if a specific key is present in a Swift dictionary, check if the corresponding value is nil or not. If myDictionary[key] != nil returns true, the key is present in this dictionary, else the key is not there.


2 Answers

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.

like image 141
Martin R Avatar answered Nov 12 '22 19:11

Martin R


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
like image 31
neoneye Avatar answered Nov 12 '22 21:11

neoneye