如何在 Swift 中覆盖 NSObject 的默认比较

How to override NSObject's default comparison in Swift

我有一个符合 NSObject, NSCoding 的自定义 class PlayerPlayer 对象用 id: String!

实例化

我遇到的一个问题是当我执行如下操作时:

let listOfPlayers = [Player]()
//populate listOfPlayers
if listOfPlayers.contains(aPlayer) ...

具体来说,contains 将 return 结果基于内存指针,而不是基于 id 值。因此,在某些情况下我会得到 true,而在其他情况下我会得到 false

我想覆盖默认比较方法,并执行了以下操作:

func isEqual(object: AnyObject?) -> Bool {
    if let otherPlayer = object as? Player {
        return self.id == otherPlayer.id
    }
    return false
}


static func == (lhs: Player, rhs: Player) -> Bool {
    return lhs.id == rhs.id
}

但是,这些函数没有被执行。我也尝试添加 override,但它 return 是一个错误“方法没有覆盖其 superclass 中的任何方法`

让客户比较器仅比较 id 元素的正确方法是什么?

谢谢!

我把下面的代码扔到了操场上。

class Player : NSObject {
    var id : String = ""

    static func == (lhs: Player, rhs: Player) -> Bool {
        print("We checked here")
        return lhs.id == rhs.id
    }
}

//Direct comparison will call our function.
if Player() == Player() {
    print("yay")
}

let players : [Player] = [Player()]

//Even though Player() has same id, it is new object in memory so this won't evaluate as true when comparing with `contains()`, 
//hence why "we checked here" isn't called again.
if players.contains(Player()) {
    print("Dang we suck")
}

//Could check with something like this if you want to check your array for a particular id.
if players.contains(where: {[=10=].id == Player().id }) {
    print("Im awesome")
}

输出:

We checked here
yay
Im awesome

我相信 [NSObject].contains() 使用三重相等的比较 === 而不是直接相等比较,后者将检查它是否是内存中的同一对象,而不是特定的 id.如您所见, We checked here 仅在直接比较时调用。您可以使用 contains(where: predicate) 方法来获取特定条件。

从 Swift 3 开始,NSObjectisEqual 方法采用 Any? 参数,所以你没有覆盖正确的方法,那就是 为什么它从未被调用。

你还应该覆盖 var hash: Int(相等的对象 必须 具有相同的散列)——否则该对象在可散列集合(集合、字典)中的行为会错误:

class Player: NSObject {
    let id: String

    init(id: String) { self.id = id }

    override func isEqual(_ object: Any?) -> Bool {
        if let other = object as? Player {
            return self.id == other.id
        } else {
            return false
        }
    }

    override var hash: Int {
        return id.hashValue
    }
}

一些测试:

let p1 = Player(id: "a")
let p2 = Player(id: "a")

print(p1 == p2) // true
print(p1 != p2) // false

// Native Swift set:
let set = Set([Player(id: "x"), Player(id: "y"), Player(id: "z")])
print(set.contains(Player(id: "y"))) // true

// Foundation set:
let nsset = NSSet(objects: Player(id: "x"), Player(id: "y"), Player(id: "z"))
print(nsset.contains(Player(id: "y"))) // true