Swift中的NSObject子类:hash vs hashValue,isEqual vs ==

当在Swift中inheritanceNSObject的时候,你应该重写hash还是Hashable? 另外,你应该重写isEqual:或实现==?

NSObject已经符合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 

我找不到一个官方的引用,但似乎hashValueNSObjectProtocol调用hash方法, ==调用isEqual:方法(从相同的协议)。 在答案的最后看到更新!

对于NSObject子类,正确的方法似乎是重写hashisEqual: ,这里是一个实验,它演示了:

1.覆盖hashValue==

 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 } 

现在创build两个不同的被认为“相等”的类的实例,并把它们放到一个集合中:

 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 

正如你所看到的, NSSetSet将对象视为不同的对象。 这不是理想的结果。 数组也有意想不到的结果:

 let nsArrayA = NSArray(object: a1) let swArrayA = [a1] print(nsArrayA.indexOfObject(a2)) // 9223372036854775807 == NSNotFound print(swArrayA.indexOf(a2)) // nil 

设置断点或添加debugging输出显示重写的==运算符永远不会被调用。 我不知道这是一个错误还是有意的行为。

2.重写hashisEqual:

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

对于Swift 3, isEqual:的定义isEqual:更改为

 override func isEqual(_ object: Any?) -> Bool { ... } 

现在所有结果都如预期的那样:

 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) 

更新:行为现在logging在“使用cocoa和Objective-C的Swift”参考中与Objective-C API交互 :

NSObject类只执行标识比较,因此您应该在从NSObject类派生的类中实现您自己的isEqual:方法。

作为实现你的类的平等的一部分,一定要根据对象比较中的规则来实现哈希属性。

实现Hashable ,这也需要你为你的types实现==运算符。 这些在Swift标准库中用作很多有用的东西,比如indexOf函数,它只适用于实现Equatabletypes的Equatable ,或者Set<T>types只适用于实现Hashabletypes。