【发布时间】:2016-11-09 03:08:40
【问题描述】:
使用 Swift 3,我有一些 NSObject 子类,我覆盖了 hash 属性和 isEqual() 函数。 (我希望这些类能够用作字典中的键,并且我希望它们的数组能够被排序,但我为什么要覆盖它们并不重要。)
回到我以前的 C++/Java 时代,我回忆起“正确的”散列涉及素数和对象属性的散列。 These questions 说说这种风格。像这样的:
override public var hash: Int {
var hash = 1
hash = hash * 17 + label.hash
hash = hash * 31 + number.hash
hash = hash * 13 + (ext?.hash ?? 0)
return hash
}
至少,我是这么认为的。在运行我的代码时,我在hash 覆盖中看到了一个非常奇怪的崩溃:
EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
查看 StackOverflow,我看到很多此类崩溃被问及,答案通常是 nil 被隐式解包,导致崩溃。但是我的哈希中没有选项。在 lldb 中玩过之后,我意识到问题是整数溢出。如果您在操场上执行此操作,您会看到它会导致错误:
`9485749857432985 * 39847239847239` // arithmetic operation '9485749857432985 * 39847239847239' (on type 'Int') results in an overflow
好吧,我在哈希覆盖中做了很多加法和乘法运算。 (在操场上很难看到,但在 lldb 中很明显是溢出导致了我的崩溃。)阅读Swift crashes due to Int overflow,我发现您可以使用&* 和&+ 来防止溢出。我不确定哈希的效果如何,但这不会崩溃,例如:
override public var hash: Int {
var hash = 1
hash = hash &* 17 &+ label.hash
hash = hash &* 31 &+ number.hash
hash = hash &* 13 &+ (ext?.hash ?? 0)
return hash
}
这是我的问题:编写这种hash 覆盖的“正确”方法是什么,没有溢出的可能性,并且实际上提供了良好的散列?
这是一个示例,您可以进入操场进行尝试。我认为这肯定会导致任何人的 EXC_BAD_INSTRUCTION:
class DateClass: NSObject {
let date1: Date
let date2: Date
let date3: Date
init(date1: Date, date2: Date, date3: Date) {
self.date1 = date1
self.date2 = date2
self.date3 = date3
}
override var hash: Int {
var hash = 1
hash = hash + 17 + date1.hashValue
hash = hash + 31 + date2.hashValue
hash = hash + 13 + date3.hashValue
return hash
}
override public func isEqual(_ object: Any?) -> Bool {
guard let rhs = object as? DateClass else {
return false
}
let lhs = self
return lhs.date1 == rhs.date1 &&
lhs.date2 == rhs.date2 &&
lhs.date3 == rhs.date3
}
}
let dateA = Date()
let dateB = Date().addingTimeInterval(10)
let dateC = Date().addingTimeInterval(20)
let dateD = Date().addingTimeInterval(30)
let dateE = Date().addingTimeInterval(40)
let class1 = DateClass(date1: dateA, date2: dateB, date3: dateC)
let class2 = DateClass(date1: dateB, date2: dateC, date3: dateD)
let class3 = DateClass(date1: dateC, date2: dateD, date3: dateE)
var dict = [DateClass: String]()
dict[class1] = "one"
dict[class2] = "two"
dict[class3] = "three"
额外问题:当你的类上的属性使用 hashValue 时,是否有适当的方法来处理 hash 值?我一直在使用它们,但我不确定这是否正确。
【问题讨论】:
标签: swift hash integer-overflow