NSObject subclass in Swift: hash vs hashValue, isEqual vs ==
lorsque vous sous-classez NSObject dans Swift, devez-vous remplacer hash ou implémenter Hashable? En outre, devez-vous outrepasser isEqual: ou implement ==?
2 réponses
NSObject
est déjà conforme au protocole Hashable
:
extension NSObject : Equatable, Hashable {
/// The hash value.
///
/// **Axiom:** `x == y` implies `x.hashValue == y.hashValue`
///
/// - Note: the hash value is not guaranteed to be stable across
/// different invocations of the same program. Do not persist the
/// hash value across program runs.
public var hashValue: Int { get }
}
public func ==(lhs: NSObject, rhs: NSObject) -> Bool
Je n'ai pas pu trouver de référence officielle, mais il semble que hashValue
appelle la méthode hash
de NSObjectProtocol
, et ==
appelle la
Méthode isEqual:
(du même protocole). voir Mise à jour au
fin de la réponse!
pour les sous-classes NSObject
, la bonne façon semble être
remplacer hash
et isEqual:
, et voici une expérience qui
démontre que:
1. Remplacer hashValue
et ==
class ClassA : NSObject {
let value : Int
init(value : Int) {
self.value = value
super.init()
}
override var hashValue : Int {
return value
}
}
func ==(lhs: ClassA, rhs: ClassA) -> Bool {
return lhs.value == rhs.value
}
maintenant créer deux instances différentes de la classe qui sont considérées "égaler" et les mettre dans un ensemble:
let a1 = ClassA(value: 13)
let a2 = ClassA(value: 13)
let nsSetA = NSSet(objects: a1, a2)
let swSetA = Set([a1, a2])
print(nsSetA.count) // 2
print(swSetA.count) // 2
comme vous pouvez le voir, les deux NSSet
et Set
traitent les objets comme différents.
Ce n'est pas le résultat souhaité. Les tableaux ont des résultats inattendus ainsi:
let nsArrayA = NSArray(object: a1)
let swArrayA = [a1]
print(nsArrayA.indexOfObject(a2)) // 9223372036854775807 == NSNotFound
print(swArrayA.indexOf(a2)) // nil
le réglage des points de rupture ou l'ajout de la sortie de débogage révèle que le dépassement
L'opérateur ==
n'est jamais appelé. Je ne sais pas si c'est un bug ou d'une
comportement voulu.
2. Remplacer hash
et isEqual:
class ClassB : NSObject {
let value : Int
init(value : Int) {
self.value = value
super.init()
}
override var hash : Int {
return value
}
override func isEqual(object: AnyObject?) -> Bool {
if let other = object as? ClassB {
return self.value == other.value
} else {
return false
}
}
}
pour Swift 3, la définition de isEqual:
changée en
override func isEqual(_ object: Any?) -> Bool { ... }
Maintenant, tous les résultats sont comme prévu:
let b1 = ClassB(value: 13)
let b2 = ClassB(value: 13)
let nsSetB = NSSet(objects: b1, b2)
let swSetB = Set([b1, b2])
print(swSetB.count) // 1
print(nsSetB.count) // 1
let nsArrayB = NSArray(object: b1)
let swArrayB = [b1]
print(nsArrayB.indexOfObject(b2)) // 0
print(swArrayB.indexOf(b2)) // Optional(0)
mise à jour: le comportement est maintenant documenté dans interaction avec les APIs objectifs dans la" Utilisation de Swift avec le cacao et L'objectif-C "référence:
la classe NSObject effectue uniquement une comparaison d'identité, vous devez donc implémenter votre propre méthode isEqual: dans les classes qui dérivent de la classe NSObject.
Dans le cadre de la mise en œuvre de l'égalité pour votre classe, assurez-vous de mettre en œuvre la propriété hash selon les règles dans la comparaison D'objet.
implémenter Hashable
, qui vous oblige également à implémenter l'opérateur ==
pour votre type. Ceux-ci sont utilisés pour beaucoup de choses utiles dans la bibliothèque standard de Swift comme la fonction indexOf
qui ne fonctionne que sur les collections d'un type qui implémente Equatable
, ou le type Set<T>
qui ne fonctionne qu'avec des types qui implémentent Hashable
.