Хеширование проблем с использованием класса-оболочки вокруг NSUUID в качестве ключа

** переписать **

Хорошо, оказывается, я действительно задаю другой вопрос. Я понимаю о hashValue и ==, так что это не актуально.

Я хотел бы мой класс обертки BUUID "делать правильные вещи" и действовать так же, как поступок NSUUID в словаре.

Смотрите ниже, где они этого не делают.

import Foundation
class BUUID: NSObject {
    init?(str: String) {
        if let uuid = NSUUID(UUIDString: str) {
            _realUUID = uuid
        }
        else {
            return nil
        }
    }

    override init() {
        _realUUID = NSUUID()
    }

    private var _realUUID: NSUUID

    override var description: String { get { return _realUUID.UUIDString } }
    override var hashValue: Int { get { return _realUUID.hashValue } }
    var UUIDString: String { get { print("WARNING Use description or .str instead"); return _realUUID.UUIDString } }
    var str: String { get { return _realUUID.UUIDString } }
}
func ==(lhs: BUUID, rhs: BUUID) -> Bool { return lhs._realUUID == rhs._realUUID }

let a = BUUID()
let b = BUUID(str: a.str)!
print("a: \(a)\nb: \(b)")
print("a === b: \(a === b)")
print("a == b: \(a == b)")

var d = [a: "Hi"]
print("\(d[a]) \(d[b])")


let nA = NSUUID()
let nB = NSUUID(UUIDString: nA.UUIDString)!
print("na: \(nA)\nnB: \(nB)")
print("nA === nB: \(nA === nB)")
print("nA == nB: \(nA == nB)")

var nD = [nA: "Hi"]
print("\(nD[nA]) \(nD[nB])")

Результаты. Обратите внимание, что я могу посмотреть с помощью NSUUID nB и верни то, что я положил под nA, Не так с моим BUUID,

a: 9DE6FE91-D4B5-4A6B-B912-5AAF34DB41C8
b: 9DE6FE91-D4B5-4A6B-B912-5AAF34DB41C8
a === b: false
a == b: true
Optional("Hi") nil

nA: <__NSConcreteUUID 0x7fa193c39500> BB9F9851-93CF-4263-B98A-5015810E4286
nB: <__NSConcreteUUID 0x7fa193c37dd0> BB9F9851-93CF-4263-B98A-5015810E4286
nA === nB: false 
nA == nB: true
Optional("Hi") Optional("Hi")

3 ответа

Решение

Наследование от NSObject также предполагает isEqual(object: AnyObject?) -> Перегрузка метода Bool:

import Foundation
class BUUID: NSObject {
    init?(str: String) {
        if let uuid = NSUUID(UUIDString: str) {
            _realUUID = uuid
        }
        else {
            return nil
        }
    }

    override init() {
        _realUUID = NSUUID()
    }

    private var _realUUID: NSUUID

    override func isEqual(object: AnyObject?) -> Bool {
        guard let buuid = object as? BUUID else {
            return false
        }

        return buuid._realUUID == _realUUID
    }
    override var description: String { get { return _realUUID.UUIDString } }
    override var hashValue: Int { get { return _realUUID.hashValue } }
    var UUIDString: String { get { print("WARNING Use description or .str instead"); return _realUUID.UUIDString } }
    var str: String { get { return _realUUID.UUIDString } }
}
func ==(lhs: BUUID, rhs: BUUID) -> Bool { return lhs._realUUID == rhs._realUUID }

let a = BUUID()
let b = BUUID(str: a.str)!
print("a: \(a)\nb: \(b)")
print("a === b: \(a === b)")
print("a == b: \(a == b)")

var d = [a: "Hi"]
print("\(d[a]) \(d[b])")


let nA = NSUUID()
let nB = NSUUID(UUIDString: nA.UUIDString)!
print("na: \(nA)\nnB: \(nB)")
print("nA === nB: \(nA === nB)")
print("nA == nB: \(nA == nB)")

var nD = [nA: "Hi"]
print("\(nD[nA]) \(nD[nB])")

Таким образом, ответ заключается в том, чтобы не делать наследование BUUID от NSObject, что подрывает быстроту переопределения ==,

Так:

extension BUUID: Hashable {}
class BUUID: CustomStringConvertible {
    // take away all 'override' keywords, nothing to override
    // otherwise same as above
}

Интересно!

Этот ответ относится к изначально заданному вопросу: почему в словаре можно получить две пары ключ-значение с одинаковыми хэш-ключами

Этот пример показывает, что ключи в словаре могут иметь одинаковые хэши, но операция равенства должна возвращать false для разных ключей:

func ==(lhs: FooKey, rhs: FooKey) -> Bool {

    return unsafeAddressOf(lhs) == unsafeAddressOf(rhs)
}

class FooKey: Hashable, Equatable {

    var hashValue: Int {

        get {
            return 123
        }
    }
}

var d = Dictionary<FooKey, String>()

let key1 = FooKey()
let key2 = FooKey()

d[key1] = "value1"
d[key2] = "value2"

Выход

[FooKey: "value1", FooKey: "value2"]

Это определенно не хорошо иметь все ключи с одинаковым хешем. В этом случае мы получаем тот наихудший случай, когда сложность поискового элемента падает до O(n) (исчерпывающий поиск). Но это будет работать.

Другие вопросы по тегам