【问题标题】:Swift 2.0 Set not working as expected when containing NSObject subclass包含 NSObject 子类时,Swift 2.0 Set 无法按预期工作
【发布时间】:2015-09-22 20:42:49
【问题描述】:

将我们的代码库升级到 Swift2 后,我遇到了不寻常的问题。 Set 没有按预期进行减法或联合。

class A: NSObject {
    let h: Int

    init(h: Int) {
        self.h = h
    }

    override var hashValue: Int {
        return h
    }
}

func ==(lhs: A, rhs: A) -> Bool {
    return lhs.hashValue == rhs.hashValue
}

let a = A(h: 1)
let b = A(h: 1)

var sa = Set([a])
let sb = Set([b])

sa.subtract(sb).count // Swift1.2 prints 0, Swift 2 prints 1

sa.contains(a) // Swift1.2 true, Swift 2 true
sa.contains(b) // Swift1.2 true, Swift 2 false

看起来 new Set 没有使用 hashValue 进行内部操作。知道这是一个错误,还是解决此问题的方法?

【问题讨论】:

    标签: swift set swift2 nsobject


    【解决方案1】:

    我玩了一下你的代码。我能够通过不再继承 NSObject 的子类,而是遵循 Hashable 协议来让它工作:

    class A: Hashable {
        let h: Int
    
        init(h: Int) {
            self.h = h
        }
    
        var hashValue: Int {
            return h
        }
    
    }
    
    func ==(lhs: A, rhs: A) -> Bool {
        return lhs.hashValue == rhs.hashValue
    }
    
    let a = A(h: 1)
    let b = A(h: 1)
    
    var sa = Set([a])
    let sb = Set([b])
    
    sa.subtract(sb).count // Swift1.2 prints 0, Swift 2 prints 1
    
    sa.contains(a) // Swift1.2 true, Swift 2 true
    sa.contains(b) // Swift1.2 true, Swift 2 false
    
    a.hashValue == b.hashValue
    

    当您从 NSObject 继承时,您的 == 重载实际上并没有被执行。如果您希望它与 NSObject 一起使用,则必须覆盖 isEquals:

    override func isEqual(object: AnyObject?) -> Bool {
        if let object = object as? A {
            return object.h == self.h
        } else  {
            return false
        }
    }
    

    【讨论】:

    • 谢谢!我遇到了 MKAnnotation 子类(也需要扩展 NSObject)的问题。你有关于这个主题的一些文档的链接吗?
    • 似乎需要 isEqual 和 hashValue 才能在 Swift 3 中正确使用 NSObject 进行 Set
    【解决方案2】:
    //: Playground - noun: a place where people can play
    
    import Foundation
    
    class X: NSObject {
        var x:String
        var y:Int
    
        init(x:String, y:Int) {
            self.x = x
            self.y = y
            super.init()
        }
    
        override var hashValue: Int {
            return x.hashValue ^ y.hashValue
        }
    
        override func isEqual(object: AnyObject?) -> Bool {
            if let rhs = object as? X {
                return x == rhs.x && y == rhs.y
            } else {
                return false
            }
        }
    }
    
    func == (lhs:X, rhs:X) -> Bool {
        return lhs.isEqual(rhs)
    }
    
    let x = X(x: "x1", y: 1)
    let y = X(x: "x1", y: 1)
    X(x: "x1", y: 1) == X(x: "x1", y: 1) // Swift 'x == y' (true)
    x.isEqual(y)                         // Obj-C '[x isEqual: y]' (true)
    
    var s:Set<X> = [X(x: "x1", y: 1)]
    s.count // count == 1
    s.insert(X(x: "x2", y: 1))
    s.count // count == 2
    s.insert(X(x: "x1", y: 1))
    s.count // count == 2
    s.insert(X(x: "x2", y: 1))
    s.count // count == 2
    

    我花时间寻找这个问题的正确答案,直到我遇到这个问题/答案。我已经恢复到 XCode Playground 中的基础知识,看看发生了什么。在 Swift 中使用 NSObject 的子类 Set 会产生一堆更具可读性的代码。

    【讨论】:

    • 我不知道为什么这么多人选择了其他答案。对我来说,这个是有价值和正确的。非常感谢。
    • 顺便提一下,在NSObjectProtocol中是必需的,如果你覆盖func isEqual(_ object: Any?) -&gt; Bool,你也必须覆盖var hash: Int { get }。就像在文档中一样,它说If two objects are equal (as determined by the isEqual(_:) method), they must have the same hash value. developer.apple.com/reference/objectivec/nsobjectprotocol/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-25
    • 1970-01-01
    相关资源
    最近更新 更多