Swift:在没有溢出崩溃的情况下覆盖NSObject哈希 [英] Swift: Overriding NSObject hash Without Overflow Crash

查看:88
本文介绍了Swift:在没有溢出崩溃的情况下覆盖NSObject哈希的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用Swift 3,我有一些NSObject子类,它们覆盖了hash属性和isEqual()函数. (我希望这些类能够用作字典中的键,并且希望它们能够被排序,但是为什么我要覆盖它们并不重要.)

Using Swift 3, I have some NSObject subclasses that I am overriding the hash property and isEqual() functions for. (I want the classes to be able to be used as keys in a dictionary, and I want an array of them to be able to be sorted, but it doesn't really matter why I'm overriding them.)

追溯到我以前的C ++/Java时代,我回想起适当的"哈希涉及素数和对象属性的哈希值. 这些

Harkening back to my old C++/Java days, I recalled that a "proper" hash involved prime numbers and the hashes of the object's properties. These questions talk about this style. Something like this:

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覆盖中看到了一个非常奇怪的崩溃:

At least, that's what I thought. While running my code, I saw a very peculiar crash in my hash override:

EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)

在这里查看StackOverflow时,我看到了很多此类崩溃问题,而答案通常是nil被隐式展开,从而导致崩溃.但是我的哈希中没有可选项.在lldb中玩了之后,我意识到问题是整数溢出.如果您在操场上这样做,将会看到它导致错误:

Looking here at StackOverflow, I saw a lot of these crashes being asked about, and the answer was usually that nil is being implicitly unwrapped, causing a crash. But there are no optionals in my hash. After playing around in lldb, I realized that the problem was integer overflow. If you do this in a playground, you'll see it causes an error:

`9485749857432985 * 39847239847239` // arithmetic operation '9485749857432985 * 39847239847239' (on type 'Int') results in an overflow

好吧,我在哈希覆盖中做了很多加法和乘法运算. (很难在操场上看到,但是在lldb中,很明显溢出导致了我的崩溃.)阅读由于Int而导致的Swift崩溃溢出,我发现可以使用&*&+来防止溢出.我不确定哈希的效果如何,但是不会崩溃,例如:

Well, I do a lot of addition and multiplication in my hash overrides. (It's hard to see in a playground, but in lldb it was obvious that overflow was causing my crash.) Reading about Swift crashes due to Int overflow, I found that you can use &* and &+ to prevent overflow. I'm not sure how well the hashes work, but this wouldn't crash, for example:

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覆盖的正确"方法是什么,而又没有溢出的可能性,并且实际上提供了良好的哈希值?

Here's my question: what is the "proper" way to write this sort of hash override, without the potential for overflow, and in a way that actually provides good hashing?

这里是一个示例,您可以弹出操场进行尝试.我认为这肯定会导致任何人使用EXC_BAD_INSTRUCTION:

Here is an example you can pop into a playground to try out. I think this would definitely lead to the EXC_BAD_INSTRUCTION for anybody:

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"

奖励问题:当类中的属性改用hashValue时,是否有适当的方法来处理hash值?我一直在互换使用它们,但不确定是否正确.

Bonus question: is there a proper way to handle making the hash value, when a property on your class uses hashValue instead? I've been using them pretty interchangeably but I'm not sure if that's correct.

推荐答案

hashValue(或hash)可以是任何东西.只要两个为isEqual返回true的对象也具有相同的哈希值.

hashValue (or hash) can really be anything. As long as two objects that return true for isEqual also have the same hash value.

您真的不必担心根据所有属性得出一些不可思议的值.

You don't really need to worry about coming up with some magic value based on all properties.

您的哈希可以简单地返回单个属性的哈希.这将避免任何形式的溢出.或者,您可以对多个属性的哈希值进行一些操作. 或",与"和异或"的某种组合.

Your hash could simply return the hash of a single property. This will avoid any sort of overflow. Or your could do some bit manipulation of the hash values of multiple properties. Some combination of "or", "and", and "xor".

关于您的奖金问题,在计算NSObject hash方法的结果时,在某些Swift数据类型上调用hashValue是没有问题的.两者都返回Int.

As for your bonus question, there is no problem calling hashValue on some Swift data types when calculating the result of the NSObject hash method. Both return Int.

这篇关于Swift:在没有溢出崩溃的情况下覆盖NSObject哈希的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆