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  .