Equatable 实现似乎不适用于泛型
Equatable implementation doesn't seem working with generics
我仍在与 Swift 泛型作斗争。今天我发现如果从泛型 class.
调用我的 Equatable 协议实现不起作用
我的模型class:
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 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:
class TrackingCache<T: Tracking>: BaseCache<Tracking> {
}
当我调用 TrackingCache
实例的 removeEntities
方法时,我总是在输出中得到 equal: false
,即使 id
是相同的。
但如果我直接将方法移动到 TrackingCache
class,它似乎工作正常!
知道为什么会发生这种情况以及如何解决这个问题吗?
注意:由于 ==
不是成员函数,默认情况下它不会为您提供动态调度,包括如果您将它与通用占位符一起使用。
考虑以下代码:
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
OK 现在创建两个 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
。选择哪个重载函数不是在运行时根据 o1
和 o2
所引用的内容动态确定的。它是由 Swift 在编译时根据 o1
和 o2
的类型确定的,在本例中是 NSObject
.
NSObject
==
的实现方式与你的 equals 不同——它调用 lhs.isEqual(rhs)
,如果没有被覆盖,它会回退到检查引用相等性(即两个引用是否指向同一个目的)。它们不是,所以它们不相等。
为什么 BaseCache
会出现这种情况,而 TrackingCache
不会出现这种情况?因为 BaseCache
被定义为仅约束 NSObject
,所以 T
仅具有 NSObject
的能力——类似于将 c1
分配给变量输入 NSObject
,将调用 ==
的 NSObject
版本。
另一方面,TrackingCache
保证 T
至少是一个 Tracking
对象,因此使用 ==
版本进行跟踪。 Swift 将选择所有可能重载中更多的 "specific" – Tracking
比它的基础 class、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)
此技术(让 ==
调用动态调度的 class 方法)也是为非 NSObject classes 实现此行为的一种方法。结构当然没有这个问题,因为它们不支持继承——结构得分 1!
我仍在与 Swift 泛型作斗争。今天我发现如果从泛型 class.
调用我的 Equatable 协议实现不起作用我的模型class:
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 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:
class TrackingCache<T: Tracking>: BaseCache<Tracking> {
}
当我调用 TrackingCache
实例的 removeEntities
方法时,我总是在输出中得到 equal: false
,即使 id
是相同的。
但如果我直接将方法移动到 TrackingCache
class,它似乎工作正常!
知道为什么会发生这种情况以及如何解决这个问题吗?
注意:由于 ==
不是成员函数,默认情况下它不会为您提供动态调度,包括如果您将它与通用占位符一起使用。
考虑以下代码:
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
OK 现在创建两个 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
。选择哪个重载函数不是在运行时根据 o1
和 o2
所引用的内容动态确定的。它是由 Swift 在编译时根据 o1
和 o2
的类型确定的,在本例中是 NSObject
.
NSObject
==
的实现方式与你的 equals 不同——它调用 lhs.isEqual(rhs)
,如果没有被覆盖,它会回退到检查引用相等性(即两个引用是否指向同一个目的)。它们不是,所以它们不相等。
为什么 BaseCache
会出现这种情况,而 TrackingCache
不会出现这种情况?因为 BaseCache
被定义为仅约束 NSObject
,所以 T
仅具有 NSObject
的能力——类似于将 c1
分配给变量输入 NSObject
,将调用 ==
的 NSObject
版本。
TrackingCache
保证 T
至少是一个 Tracking
对象,因此使用 ==
版本进行跟踪。 Swift 将选择所有可能重载中更多的 "specific" – Tracking
比它的基础 class、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)
此技术(让 ==
调用动态调度的 class 方法)也是为非 NSObject classes 实现此行为的一种方法。结构当然没有这个问题,因为它们不支持继承——结构得分 1!