为什么我要添加!=来使Equatable工作?

时间:2017-08-09 02:54:25

标签: swift

为什么我必须添加!=才能使比较正确?

import UIKit

class Person: NSObject {
    var name: String
    var age: Int

    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

extension Person {
    static func ==(lhs: Person, rhs: Person) -> Bool {
        return lhs.name == rhs.name && lhs.age == rhs.age
    }
    static func !=(lhs: Person, rhs: Person) -> Bool {
        return !(lhs == rhs)
    }
}

let first = Person(name: "John", age: 26) 
let second = Person(name: "John", age: 26)

/**
 * return false (which is correct) when we implement != function. But,
 * it will return true if we don't implement the != function.
 */
first != second 

更新 所以我知道为什么我必须添加!=函数才能使它工作。这是因为该类继承了在场景后使用NSObject方法的isEqual。但是为什么添加!=函数会使它工作?这里有任何解释吗?

3 个答案:

答案 0 :(得分:3)

NSObject符合Equatable但使用自己的isEqual方法,就isEqual而言,两个实例都不相等。仅当NSObject的表单已实施且包含==时,!=才会调用==

如果您删除NSObject(并添加Equatable),则==的实施工作正常。

NSObject的推荐方法是使用自定义实现覆盖isEqual并省略==(以及!=)。

答案 1 :(得分:2)

很抱歉,这不是您问题的直接答案。

正如Alexander评论的那样,Swift标准库具有!=的默认实现:

Equatable.swift

  @_transparent
  public static func != (lhs: Self, rhs: Self) -> Bool {
    return !(lhs == rhs)
  }

我无法很好地解释这种行为,但上面默认实现中的==运算符已解决为==的默认NSObject运算符,NSObject(以及它的后代已经Equatable并且有==运算符符合Equatable。因此,即使显式表示与您的!=定义完全相同,==运算符也会针对不同的实现进行求解。

用于定义自己与NSObject - 后代类的相等性的通用指南:

使==isEqual(_:)保持一致

您可以将班级的实例存储在NSArrayNSDictionary内(在很多情况下会隐式)。在他们的方法中,当需要进行相等性检查时使用isEqual(_:),而不是==运算符。

因此,只需定义==运算符而不向isEqual(_:)提供一致的覆盖,此类方法将产生意外结果。

要保持一致==isEqual(_:)

只覆盖isEqual(_:),不要明确定义==!=

==(以及NSObject!=的默认实施使用isEqual(_:)

class Person: NSObject {
    var name: String
    var age: Int

    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }

    override func isEqual(_ object: Any?) -> Bool {
        if let other = object as? Person {
            return self.name == other.name && self.age == other.age
        }
        return false
    }
}

(请参阅底部的 ONE MORE THING 。)

<强> ADDITION

可以在非NSObject类上找到类似的行为。

class BaseClass {
    var a: Int

    init(a: Int) {
        self.a = a
    }
}
extension BaseClass: Equatable {
    static func == (lhs: BaseClass, rhs: BaseClass) -> Bool {
        print("`==` of BaseClass")
        return lhs.a == rhs.a
    }
}
let b1 = BaseClass(a: 0)
let b2 = BaseClass(a: 0)
print(b1 != b2) //->`==` of BaseClass, false ### as expected

class DerivedClass: BaseClass {
    var b: Int

    init(a: Int, b: Int) {
        self.b = b
        super.init(a: a)
    }
}
extension DerivedClass {
    static func == (lhs: DerivedClass, rhs: DerivedClass) -> Bool {
        print("`==` of DerivedClass")
        return lhs.a == rhs.a && lhs.b == rhs.b
    }
}
let d1 = DerivedClass(a: 0, b: 1)
let d2 = DerivedClass(a: 0, b: 2)
print(d1 != d2) //->`==` of BaseClass, false ### `==` of DerivedClass and true expected

对于已经==类重写Equatable,我们需要格外小心。

还有一件事

(感谢Hamish。)

您知道在创建符合==的类型时,您需要始终如一地实施hashValueHashableNSObject被声明为Hashable,其hashValue需要与hash一致。因此,当您覆盖isEqual(_:) - 后代中的NSObject时,您还应覆盖与被覆盖的hash一致的isEqual(_:)

所以,你的Person类应该是这样的:

class Person: NSObject {
    var name: String
    var age: Int

    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }

    override func isEqual(_ object: Any?) -> Bool {
        if let other = object as? Person {
            return self.name == other.name && self.age == other.age
        }
        return false
    }

    override var hash: Int {
        //### This is just an example, but not too bad in practical use cases.
        return name.hashValue ^ age.hashValue
    }
}

答案 2 :(得分:1)

你在做什么是错的。您不应该实施==!=。 NSObject子类自动将==实现为isEqual:。你正在扰乱这一点。你应该实现isEqual:,这就是全部。