使用Swift 3,我有一些NSObject
的子类,我重写了hash
属性和isEqual()
函数。 (我希望这些类可以用作字典中的键,并且我希望能够对它们进行排序,但是为什么我会覆盖它们并不重要)。Swift:覆盖NSObject散列没有溢出崩溃
回想我的旧C++/Java时代,我回忆说,一个“正确”散列涉及素数和对象属性的哈希。 Thesequestions谈论这种风格。就像这样:
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,我看到被问了很多这些崩溃有关,答案通常是零正在隐含地解开,导致崩溃。但是我的散列中没有可选项。在玩过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"
奖金的问题:是否有处理使得hash
值取之有道,当你的类属性使用hashValue
呢?我一直在相互交换使用它们,但我不确定这是否正确。
只要'isEqual'为true,哈希值就是相同的。你如何为我上面的'DateClass'做到这一点,在执行'isEqual'时考虑到所有属性,而不考虑散列中的所有属性? – nickjwallin
你的'isEqual'方法很好。鉴于此,您的'hash'可能会为每个对象返回'1',并且符合条件(实际上不会这样做,因为它会产生性能问题)。请记住,条件是相同的对象具有相同的散列。相反的情况并不一定是真的。具有相同散列的对象并不相同,这是完全正确的。 – rmaddy