使用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