Swift 中的 NSObject 子类:hash 与 hashValue,isEqual 与 ==
NSObject subclass in Swift: hash vs hashValue, isEqual vs ==
在 Swift 中子类化 NSObject
时,您应该覆盖 hash
还是实现 Hashable
?
此外,您应该覆盖 isEqual:
还是实施 ==
运算符?
实现 Hashable
,这还需要您为您的类型实现 ==
运算符。这些用于 Swift 标准库中的许多有用的东西,例如 indexOf
函数,它仅适用于实现 Equatable
或 Set<T>
类型的类型的集合它仅适用于实现 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
从 NSObjectProtocol
调用 hash
方法,==
调用
isEqual:
方法(来自相同的协议)。 查看更新
回答完毕!
对于NSObject
子classes,正确的做法好像是
覆盖 hash
和 isEqual:
,这是一个实验
证明:
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
}
现在创建 class 的两个不同实例
“相等”并将它们放入集合中:
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
都将对象视为不同的对象。
这不是想要的结果。数组也有意想不到的结果:
let nsArrayA = NSArray(object: a1)
let swArrayA = [a1]
print(nsArrayA.indexOfObject(a2)) // 9223372036854775807 == NSNotFound
print(swArrayA.indexOf(a2)) // nil
设置断点或添加调试输出显示覆盖
==
运算符永远不会被调用。我不知道这是一个错误还是
预期行为。
2。覆盖 hash
和 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
}
}
}
对于Swift3,定义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)
更新: 该行为记录在“Using Swift with Cocoa and Objective-C”一书中,在“Interacting with Objective-C API":
The default implementation of the ==
operator invokes the isEqual:
method, and the default implementation of the ===
operator checks pointer equality. You should not override the equality or identity operators for types imported from Objective-C.
The base implementation of the isEqual:
provided by the NSObject
class is equivalent to an identity check by pointer equality. You can override isEqual:
in a subclass to have Swift and Objective-C APIs determine equality based on the contents of objects rather than their identities.
可以在 Apple Book 应用程序中找到这本书。
它也记录在 Apple 的网站上,但已被删除,并且在页面的 WebArchive snapshot 上仍然可见。
对于 NSObject
,最好覆盖 hash
和 isEqual
。它已经符合 Hashable
和 Equatable
并且已经综合了反过来调用 hash
和 isEqual
的一致性。因此,由于它是一个 NSObject
,所以按照 ObjC 的方式进行操作并覆盖也会影响 ObjC 哈希值和相等性的值。
class Identity: NSObject {
let name: String
let email: String
init(name: String, email: String) {
self.name = name
self.email = email
}
override var hash: Int {
var hasher = Hasher()
hasher.combine(name)
hasher.combine(email)
return hasher.finalize()
}
override func isEqual(_ object: Any?) -> Bool {
guard let other = object as? Identity else {
return false
}
return name == other.name && email == other.email
}
}
“另外,你应该覆盖 isEqual: 还是实现 ==?”
你可以两者都做。如果您要实施
行为不同,您也会为代码用户的生活增添光彩。去过也做过。很好玩
在 Swift 中子类化 NSObject
时,您应该覆盖 hash
还是实现 Hashable
?
此外,您应该覆盖 isEqual:
还是实施 ==
运算符?
实现 Hashable
,这还需要您为您的类型实现 ==
运算符。这些用于 Swift 标准库中的许多有用的东西,例如 indexOf
函数,它仅适用于实现 Equatable
或 Set<T>
类型的类型的集合它仅适用于实现 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
从 NSObjectProtocol
调用 hash
方法,==
调用
isEqual:
方法(来自相同的协议)。 查看更新
回答完毕!
对于NSObject
子classes,正确的做法好像是
覆盖 hash
和 isEqual:
,这是一个实验
证明:
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
}
现在创建 class 的两个不同实例 “相等”并将它们放入集合中:
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
都将对象视为不同的对象。
这不是想要的结果。数组也有意想不到的结果:
let nsArrayA = NSArray(object: a1)
let swArrayA = [a1]
print(nsArrayA.indexOfObject(a2)) // 9223372036854775807 == NSNotFound
print(swArrayA.indexOf(a2)) // nil
设置断点或添加调试输出显示覆盖
==
运算符永远不会被调用。我不知道这是一个错误还是
预期行为。
2。覆盖 hash
和 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
}
}
}
对于Swift3,定义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)
更新: 该行为记录在“Using Swift with Cocoa and Objective-C”一书中,在“Interacting with Objective-C API":
The default implementation of the
==
operator invokes theisEqual:
method, and the default implementation of the===
operator checks pointer equality. You should not override the equality or identity operators for types imported from Objective-C.The base implementation of the
isEqual:
provided by theNSObject
class is equivalent to an identity check by pointer equality. You can overrideisEqual:
in a subclass to have Swift and Objective-C APIs determine equality based on the contents of objects rather than their identities.
可以在 Apple Book 应用程序中找到这本书。
它也记录在 Apple 的网站上,但已被删除,并且在页面的 WebArchive snapshot 上仍然可见。
对于 NSObject
,最好覆盖 hash
和 isEqual
。它已经符合 Hashable
和 Equatable
并且已经综合了反过来调用 hash
和 isEqual
的一致性。因此,由于它是一个 NSObject
,所以按照 ObjC 的方式进行操作并覆盖也会影响 ObjC 哈希值和相等性的值。
class Identity: NSObject {
let name: String
let email: String
init(name: String, email: String) {
self.name = name
self.email = email
}
override var hash: Int {
var hasher = Hasher()
hasher.combine(name)
hasher.combine(email)
return hasher.finalize()
}
override func isEqual(_ object: Any?) -> Bool {
guard let other = object as? Identity else {
return false
}
return name == other.name && email == other.email
}
}
“另外,你应该覆盖 isEqual: 还是实现 ==?”
你可以两者都做。如果您要实施 行为不同,您也会为代码用户的生活增添光彩。去过也做过。很好玩