NSObject子类中的Swift:hash vs hashValue,isEqual vs == [英] NSObject subclass in Swift: hash vs hashValue, isEqual vs ==

查看:657
本文介绍了NSObject子类中的Swift:hash vs hashValue,isEqual vs ==的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当子类化NSObject在Swift,应该覆盖哈希还是实现Hashable?此外,你应该覆写isEqual:或implement ==?

解决方案

NSObject 已经符合 Hashable 协议:

  Hashable {
///哈希值。
///
/// ** Axiom:**`x == y`意味着`x.hashValue == y.hashValue`
///
// / - 注意:哈希值不能保证在同一程序的
///不同调用之间是稳定的。不要在程序运行时保持
///哈希值。
public var hashValue:Int {get}
}

public func ==(lhs:NSObject,rhs:NSObject) - > Bool



我找不到官方参考,但似乎 hashValue
NSObjectProtocol 调用 hash == 调用
isEqual:方法(来自同一协议)。 查看
结尾的更新!



NSObject 子类,正确的方法似乎是
重写 hash isEqual:,这里是一个实验其中
证明:



1。覆盖 hashValue ==



 code> class ClassA:NSObject {
let value:Int

init(value:Int){
self.value = value
super.init )
}

override var hashValue:Int {
返回值
}
}

func ==(lhs: ClassA,rhs:ClassA) - > Bool {
return lhs.value == rhs.value
}

创建两个不同的类实例,它们被认为是
等于并将它们放入一个集合:

  let a1 = classA(value:13)
let a2 = ClassA(value:13)

let nsSetA = NSSet(objects:a1,a2)
let swSetA = Set ,a2])

print(nsSetA.count)// 2
print(swSetA.count)// 2

如您所见, NSSet 设置对象不同。
这不是所需的结果。数组也有意想不到的结果:

  let nsArrayA = NSArray(object:a1)
let swArrayA = [a1]

print(nsArrayA.indexOfObject(a2))// 9223372036854775807 == NSNotFound
print(swArrayA.indexOf(a2))// nil

设置断点或添加调试输出显示重写的
== 称为。我不知道这是一个错误还是
的预期行为。



2。覆盖 hash isEqual:



 code> class ClassB:NSObject {
let value:Int

init(value:Int){
self.value = value
super.init )
}

override var hash:Int {
返回值
}

override func isEqual(object:AnyObject?) - > ; Bool {
如果let other = object as? ClassB {
return self.value == other.value
} else {
return false
}
}
}

Swift 3 的定义 isEqual:更改为

  override func isEqual(_ object:Any?) - > Bool {...} 

现在所有结果都符合预期:



let b1 = ClassB(value:13)
let b2 = ClassB(value:13)

让nsSetB = NSSet (对象:b1,b2)
让swSetB = Set([b1,b2])

打印(swSetB.count)// 1
打印(nsSetB.count) / 1

let nsArrayB = NSArray(object:b1)
let swArrayB = [b1]

print(nsArrayB.indexOfObject(b2))// 0
print(swArrayB.indexOf(b2))//可选(0)






更新:现在,行为记录在
与Objective-C API交互
在使用Swift与Cocoa和Objective-C引用:


NSObject类仅执行身份比较,因此您应该实现自己的isEqual:从NSObject类派生的类。



作为实现类的相等性的一部分,请务必根据对象比较中的规则实现hash属性。



When subclassing NSObject in Swift, should you override hash or implement Hashable? Also, should you override isEqual: or implement ==?

解决方案

NSObject already conforms to the Hashable protocol:

extension NSObject : Equatable, Hashable {
    /// The hash value.
    ///
    /// **Axiom:** `x == y` implies `x.hashValue == y.hashValue`
    ///
    /// - Note: the hash value is not guaranteed to be stable across
    ///   different invocations of the same program.  Do not persist the
    ///   hash value across program runs.
    public var hashValue: Int { get }
}

public func ==(lhs: NSObject, rhs: NSObject) -> Bool

I could not find an official reference, but it seems that hashValue calls the hash method from NSObjectProtocol, and == calls the isEqual: method (from the same protocol). See update at the end of the answer!

For NSObject subclasses, the correct way seems to be to override hash and isEqual:, and here is an experiment which demonstrates that:

1. Override hashValue and ==

class ClassA : NSObject {
    let value : Int

    init(value : Int) {
        self.value = value
        super.init()
    }

    override var hashValue : Int {
        return value
    }
}

func ==(lhs: ClassA, rhs: ClassA) -> Bool {
    return lhs.value == rhs.value
}

Now create two different instances of the class which are considered "equal" and put them into a set:

let a1 = ClassA(value: 13)
let a2 = ClassA(value: 13)

let nsSetA = NSSet(objects: a1, a2)
let swSetA = Set([a1, a2])

print(nsSetA.count) // 2
print(swSetA.count) // 2

As you can see, both NSSet and Set treat the objects as different. This is not the desired result. Arrays have unexpected results as well:

let nsArrayA = NSArray(object: a1)
let swArrayA = [a1]

print(nsArrayA.indexOfObject(a2)) // 9223372036854775807 == NSNotFound
print(swArrayA.indexOf(a2)) // nil

Setting breakpoints or adding debug output reveals that the overridden == operator is never called. I don't know if this is a bug or intended behavior.

2. Override hash and isEqual:

class ClassB : NSObject {
    let value : Int

    init(value : Int) {
        self.value = value
        super.init()
    }

    override var hash : Int {
        return value
    }

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

For Swift 3, the definition of isEqual: changed to

override func isEqual(_ object: Any?) -> Bool { ... }

Now all results are as expected:

let b1 = ClassB(value: 13)
let b2 = ClassB(value: 13)

let nsSetB = NSSet(objects: b1, b2)
let swSetB = Set([b1, b2])

print(swSetB.count) // 1
print(nsSetB.count) // 1

let nsArrayB = NSArray(object: b1)
let swArrayB = [b1]

print(nsArrayB.indexOfObject(b2)) // 0
print(swArrayB.indexOf(b2)) // Optional(0)


Update: The behavior is now documented in Interacting with Objective-C APIs in the "Using Swift with Cocoa and Objective-C" reference:

The NSObject class only performs an identity comparison, so you should implement your own isEqual: method in classes that derive from the NSObject class.

As part of implementing equality for your class, be sure to implement the hash property according to the rules in Object comparison.

这篇关于NSObject子类中的Swift:hash vs hashValue,isEqual vs ==的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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