2015-05-20 37 views
10

chiave Dizionario richiede Hashable conformità:di riferimento come chiave nel dizionario rapida

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

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

Come ottenere l'indirizzo di pura istanza di classe Swift da utilizzare come hashValue?

risposta

15

Per Swift 3 (Xcode 8 beta 6 o successivo), utilizzare 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 
} 

Poi a == b sse a e b riferiscono alla stessa istanza della classe ( e a.hashValue == b.hashValue è soddisfatta in questo caso).

Esempio:

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

per SWIFT 2.3 e versioni precedenti, è possibile utilizzare

/// 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> 

per implementare un valore hash, e l'operatore identità === al implementare il protocollo Equatable.

+0

'unsafeAddressOf' è quello che ho sempre cercato. Grazie! – Kirsteins

+0

Ci sarà un caso in cui 'unsafeAddressOf' restituirà un indirizzo diverso per la stessa variabile? Mi sembra di aver incontrato questo problema e non riesco più a riprodurlo. L'indirizzo direbbe una cosa all'inizio e poi cambierebbe, come se Swift spostasse la variabile in memoria altrove. – pixelfreak

+0

Non per istanze di * classe *. Ma vedi http://stackoverflow.com/questions/32638879/swift-strings-and-memory-addresses per un caso in cui accadrebbe per determinati * tipi di valori * che sono implicitamente sottoposti a bridge su alcuni NSObject. –

0

Se non si vuole o non può attuare Hashable per qualche ragione è facile da usare un C helper Obiettivo:

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

long mappe per Swift Int e può essere utilizzato perfettamente come chiave Swift Dictionary su entrambe le architetture a 32 e 64 bit. E 'stata la soluzione più veloce che ho trovato durante la profilazione di diversi approcci, tra cui unsafeAddress. Nel mio caso la prestazione era il criterio principale.

1

Swift 3

Questa tratta dal grande frammento di codice in Martin R's answer con commento perspicace da 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