2016-11-09 4 views
0

스위프트 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를 사용하는 경우,이? 나는 꽤 상호 교환 적으로 그것들을 사용하고 있었지만 그것이 맞는지 확실하지 않습니다.

답변

0

hashValue (또는 hash)은 실제로 아무것도 될 수 있습니다. isEqual에 대해 true을 반환하는 두 객체의 해시 값이 같아야합니다.

모든 속성에 기반한 마법의 가치를 생각해 볼 필요가 없습니다.

해시는 단일 속성의 해시를 반환 할 수 있습니다. 이렇게하면 어떤 종류의 넘침도 피할 수 있습니다. 또는 여러 속성의 해시 값에 대한 비트 조작을 수행 할 수 있습니다. "or", "and"및 "xor"의 일부 조합입니다.

보너스 질문의 경우 NSObject hash 메서드의 결과를 계산할 때 을 일부 Swift 데이터 형식으로 호출해도 문제가 없습니다. 둘 다 Int입니다.

+0

isEqual이 true 일 때 해시는 동일 할 수 있습니다. 위의'DateClass'에 대해 어떻게해야할까요? 해시에서 모든 속성을 고려하지 않고 isEqual을 할 때 모든 속성을 고려해야합니까? – nickjwallin

+0

'isEqual' 메소드는 그대로 유지됩니다. 그것을 감안할 때, 당신의'hash'는 모든 객체에 대해'1'을 반환 할 수 있고 그 조건은 충족됩니다 (실제로 성능 문제가 있기 때문에 그렇게하지 마십시오). 조건은 동일한 객체가 동일한 해시를 가짐을 기억하십시오. 그 반대는 사실 일 필요가 없습니다. 동일하지 않은 동일한 해시를 가진 객체를 갖는 것은 완벽합니다. – rmaddy