스위프트 3을 사용하면 hash
속성과 isEqual()
기능을 재정의하는 일부 NSObject
하위 클래스가 있습니다. (클래스를 사전의 키로 사용할 수있게하려면 배열을 정렬 할 수 있기를 원하지만 실제로 왜 재정의하는지는 중요하지 않습니다.)스위프트 : 오버플로 크래시가없는 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
}
적어도 그것이 내가 생각한 것입니다. StackOverflow에 이곳을 찾고
EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
, 나는 이러한 충돌의 많은에 대한 요청을 받고보고, 그 전무가되고있는 대답은 보통이었다 : 내 코드를 실행하는 동안, 내 hash
재정에 매우 독특한 충돌을 보았다 암시 적으로 풀려서 충돌을 일으켰습니다. 그러나 내 해쉬에는 선택 사항이 없습니다. 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