在Swift中将NSObject子类化时,您应该重写哈希还是实现Hashable?另外,您应该重写isEqual:还是实现==?
NSObject已经符合Hashable协议:
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
我找不到正式的参考资料,但似乎从中hashValue 调用了hash方法NSObjectProtocol,并从相同的协议中==调用了该 isEqual:方法。 请参阅答案末尾的更新!
hashValue
hash
NSObjectProtocol
==
isEqual:
对于NSObject子类,正确的方法似乎是重写hash和isEqual:,这是一个实验,证明了这一点:
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 }
现在,创建该类的两个不同的实例,它们被认为是“相等的”,并将它们放入一个集合中:
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
正如你所看到的,NSSet并且Set治疗的对象不同。这不是期望的结果。数组也有意外的结果:
NSSet
Set
let nsArrayA = NSArray(object: a1) let swArrayA = [a1] print(nsArrayA.indexOfObject(a2)) // 9223372036854775807 == NSNotFound print(swArrayA.indexOf(a2)) // nil
设置断点或添加调试输出表明,==永远不会调用覆盖的 运算符。我不知道这是错误还是预期的行为。
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:更改为
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)
更新: 该行为现在在 “与Swift与Cocoa和Objective-C一起使用”参考中与Objective-C API交互中进行了记录 :
NSObject类仅执行身份比较,因此您应该在从NSObject类派生的类中实现自己的isEqual:方法。 作为为类实现相等性的一部分,请确保根据对象比较中的规则实现hash属性。
NSObject类仅执行身份比较,因此您应该在从NSObject类派生的类中实现自己的isEqual:方法。
作为为类实现相等性的一部分,请确保根据对象比较中的规则实现hash属性。