我仍然在与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?
.....
}
Class,它使用泛型类型:
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()
}
}
它的子类:
class TrackingCache<T: Tracking>: BaseCache<Tracking> {
}
当我调用removeEntities
实例的TrackingCache
方法时,即使equal: false
s相同,我也总是在输出中得到id
。
但是,如果我直接将方法移到TrackingCache
类,它似乎工作正常!
为什么会发生这种情况以及如何解决这个问题?
答案 0 :(得分: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
现在确定创建两个NSObject
类型的变量,并为它们分配相同的值:
let o1: NSObject = c1
let o2: NSObject = c2
// this will be false
o1 == o2
为什么呢?因为您正在调用函数func ==(lhs: NSObject, rhs: NSObject) -> Bool
,不是 func ==(lhs: C, rhs: C) -> Bool
。根据{{1}}和o1
引用的内容,在运行时不会动态确定要选择的重载函数。它由Swift在编译时根据o2
和o1
的类型确定,在本例中为o2
。
NSObject
NSObject
的实现方式与您的equals不同 - 它调用==
,如果不覆盖检查引用相等性(即指向同一对象的两个引用),则会回退。他们不是,所以他们不平等。
为什么lhs.isEqual(rhs)
会发生这种情况,BaseCache
会发生这种情况?由于TrackingCache
被定义为仅约束BaseCache
,因此NSObject
仅具有T
的功能 - 类似于您将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
如果您想解决此问题,可以覆盖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)
这种技术(让==
调用动态调度的类方法)也是一种为非NSObject类实现此行为的方法。当然,结构没有这个问题,因为它们不支持继承 - 结构得分为1!