等同的实现似乎不适用于泛型

And*_*eev 6 generics ios swift

我仍然在与Swift仿制药作斗争.今天我发现我的Equatable协议实现不起作用,如果它是从泛型类调用的.

我的模特课:

func ==(lhs: Tracking, rhs: Tracking) -> Bool {
    // This method never executes if called from BaseCache
    return lhs.id == rhs.id 
}

class Tracking: NSObject, Equatable, Printable {
    var id: String?
    ..... 
}
Run Code Online (Sandbox Code Playgroud)

类,使用泛型类型:

class BaseCache<T: NSObject where T: Equatable, T: Printable>  {

    .....

    func removeEntities(entities: [T]) {
        var indexesToRemove = [Int]()
        for i in 0...allEntities.count - 1 {
            let item = allEntities[i]
            for entity in entities {
                println("equal: \(entity == item)")
                // FOR SOME REASONS THE STATEMENT BELOW IS ALWAYS FALSE
                if entity == item {
                    indexesToRemove.append(i)
                    break
                }
            }
        }
        for index in indexesToRemove {
            allEntities.removeAtIndex(index)
        }
        didRemoveEntities()
    }
}
Run Code Online (Sandbox Code Playgroud)

它的子类:

class TrackingCache<T: Tracking>: BaseCache<Tracking> {
}
Run Code Online (Sandbox Code Playgroud)

当我调用实例的removeEntities方法时TrackingCache,我总是进入equal: false输出,即使ids是相同的.

但是,如果我TrackingCache直接将方法移动到类,它似乎工作正常!

任何想法为什么会发生这种情况以及如何解决这个问题?

Air*_*ity 12

注意:因为==它不是成员函数,所以默认情况下它不会给你动态调度,包括你将它与通用占位符一起使用.

请考虑以下代码:

class C: NSObject, Equatable {
    let id: Int
    init(_ id: Int) { self.id = id }
}

// define equality as IDs are equal
func ==(lhs: C, rhs: C) -> Bool {
    return lhs.id == rhs.id 
}

// create two objects with the same ID
let c1 = C(1)
let c2 = C(1)

// true, as expected
c1 == c2
Run Code Online (Sandbox Code Playgroud)

好的,现在创建两个类型的变量NSObject,并为它们分配相同的值:

let o1: NSObject = c1
let o2: NSObject = c2

// this will be false
o1 == o2
Run Code Online (Sandbox Code Playgroud)

为什么?因为您正在调用该函数func ==(lhs: NSObject, rhs: NSObject) -> Bool,而不是 func ==(lhs: C, rhs: C) -> Bool.选择哪个重载函数不是在运行时根据内容o1o2引用动态确定的.它是由夫特在编译时基于所述类型的确定,o1以及o2,在这种情况下是NSObject.

NSObject ==对你的equals实现不同 - 它调用lhs.isEqual(rhs),如果没有被覆盖,则检查引用相等(即两个引用指向同一个对象).他们不是,所以他们不平等.

为什么会发生这种情况BaseCache而不是发生TrackingCache?因为BaseCache被定义为仅限制NSObject,因此T只具有a的功能NSObject- 类似于分配c1给类型变量时NSObject,将调用其NSObject版本==.

TrackingCache另一方面,保证T将至少是一个Tracking对象,因此==使用跟踪的版本.Swift将挑选所有可能重载的更"具体" - Tracking比它的基类更具体NSObject.

这是一个更简单的例子,只是通用函数:

func f<T: NSObject>(lhs: T, rhs: T) -> Bool {
    return lhs == rhs
}

func g<T: C>(lhs: T, rhs: T) -> Bool {
    return lhs == rhs
}

f(c1, c2) // false
g(c1, c2) // true
Run Code Online (Sandbox Code Playgroud)

如果要解决此问题,可以覆盖isEqual:

class C: NSObject, Equatable {
    ...
    override func isEqual(object: AnyObject?) -> Bool {
        return (object as? C)?.id == id
    }
}

// this is now true:
o1 == o2
// as is this:
f(c1, c2)
Run Code Online (Sandbox Code Playgroud)

这种技术(==调用动态调度的类方法)也是一种为非NSObject类实现此行为的方法.当然,结构没有这个问题,因为它们不支持继承 - 结构得分为1!