Swift 2.0 中等号运算符和 NSObjects 的错误?
Bug with equals operator and NSObjects in Swift 2.0?
好的,在 Swift 2.0 中为 NSObject subclasses 编写自己的等于运算符时会发生一些奇怪的事情,如下所示:
func ==(lhs: MyObject, rhs: MyObject) -> Bool {
return lhs.identifier == rhs.identifier
}
对于看起来像这样的 class:
class MyObject: NSObject {
let identifier: String
init(identifier: String) {
self.identifier = identifier
}
}
这在 Swift 1.2 及以下版本中工作得很好。它仍然有效:
let myObject1 = MyObject(identifier: "A")
let myObject2 = MyObject(identifier: "A")
let result = (myObject1 == myObject2)
// result is true
到目前为止还不错,但是如果两个变量都是可选的呢?
let myObject1: MyObject? = MyObject(identifier: "A")
let myObject2: MyObject? = MyObject(identifier: "A")
let result = (myObject1 == myObject2)
// result is false, equals operator was never even called
还有一件事不再有效:
let myObject1 = MyObject(identifier: "A")
let myObject2 = MyObject(identifier: "A")
let result = (myObject1 == myObject2)
// result is true
let result = (myObject1 != myObject2)
// result is true, equals operator was never even called
很明显,!= 不再调用 == 运算符并将其取反。使用 !=
时似乎只是比较实例
只有当您的 class 是 NSObject 的子class(直接或间接)时,所有这一切才会发生。如果不是,一切都会像您期望的那样工作。
谁能告诉我这是 Swift 2.0 中的新 'feature' 还是一个讨厌的错误?
不幸的是,我不知道这是否被视为一项功能(我不这么认为)。如果任何 class subclass 是符合 Equatable
的 class(如 NSObject
;它比较实际实例),则会出现此问题。因此,如果您仅 "override" subclass 的 ==
运算符,则所有其他运算符如:
func !=<T : Equatable>(lhs: T, rhs: T) -> Bool
func ==<T : Equatable>(lhs: T?, rhs: T?) -> Bool
func ==<T : Equatable>(lhs: [T], rhs: [T]) -> Bool
其中 T
被限制为 Equatable
Swift 使用基数 class 的 ==
运算符。作为(耗时的)解决方法,您可以重载所有必须使用的相等运算符,如下所示:
func !=(lhs: MyObject, rhs: MyObject) -> Bool { ... }
func ==(lhs: MyObject?, rhs: MyObject?) -> Bool { ... }
func ==(lhs: [MyObject], rhs: [MyObject]) -> Bool { ... }
编辑:原因
这种行为的原因是,如果一个子class符合Equatable
,则自我要求的Self
被确定为这个class。因此,每次使用符合 Equatable
的(通用)类型调用 ==
时,它只会调用初始符合 class.
的运算符
我认为这种行为应该被认为是一个错误(从 Xcode 7 beta 6 开始仍然存在),但有一个临时的解决方法:重写 NSObject 的 -isEqual
而不是实现 Swift 的 ==
运算符。
class MyObject: NSObject {
let identifier: String
init(identifier: String) {
self.identifier = identifier
}
override func isEqual(object: AnyObject?) -> Bool {
guard let rhs = object as? MyObject else {
return false
}
let lhs = self
return lhs.identifier == rhs.identifier
}
}
我在此处找到了该问题的另一个参考资料,其中包含更多代码示例:http://mgrebenets.github.io/swift/2015/06/21/equatable-nsobject-with-swift-2/
does not work with NSManagedObject
(explained here),所以我创建了一个新协议 NSObjectSubclassEquatable
,您可以使用它来比较 NSobject
子类。
infix operator =~= {}
public protocol NSObjectSubclassEquatable {
static func compare(lhs: Self,_ rhs: Self) -> Bool
}
public func =~=<T : NSObjectSubclassEquatable>(lhs: T, rhs: T) -> Bool {
return T.compare(lhs, rhs)
}
func =~=<Element : NSObjectSubclassEquatable>(lhs: [Element], rhs: [Element]) -> Bool {
for (lhsElement,rhsElement) in zip(lhs, rhs) {
if !(lhsElement =~= rhsElement) {
return false
}
}
return true
}
示例:
class Point: NSObject {
var x: Int
var y: Int
init(_ x: Int,_ y: Int) {
self.x = x
self.y = y
}
}
extension Point: NSObjectSubclassEquatable {
static func compare(lhs: Point,_ rhs: Point) -> Bool {
return lhs.x == rhs.x && lhs.y == rhs.y
}
}
Point(1,2) =~= Point(1,2) // Prints true
[Point(1,2)] =~= [Point(1,2)] // Prints true
好的,在 Swift 2.0 中为 NSObject subclasses 编写自己的等于运算符时会发生一些奇怪的事情,如下所示:
func ==(lhs: MyObject, rhs: MyObject) -> Bool {
return lhs.identifier == rhs.identifier
}
对于看起来像这样的 class:
class MyObject: NSObject {
let identifier: String
init(identifier: String) {
self.identifier = identifier
}
}
这在 Swift 1.2 及以下版本中工作得很好。它仍然有效:
let myObject1 = MyObject(identifier: "A")
let myObject2 = MyObject(identifier: "A")
let result = (myObject1 == myObject2)
// result is true
到目前为止还不错,但是如果两个变量都是可选的呢?
let myObject1: MyObject? = MyObject(identifier: "A")
let myObject2: MyObject? = MyObject(identifier: "A")
let result = (myObject1 == myObject2)
// result is false, equals operator was never even called
还有一件事不再有效:
let myObject1 = MyObject(identifier: "A")
let myObject2 = MyObject(identifier: "A")
let result = (myObject1 == myObject2)
// result is true
let result = (myObject1 != myObject2)
// result is true, equals operator was never even called
很明显,!= 不再调用 == 运算符并将其取反。使用 !=
时似乎只是比较实例只有当您的 class 是 NSObject 的子class(直接或间接)时,所有这一切才会发生。如果不是,一切都会像您期望的那样工作。
谁能告诉我这是 Swift 2.0 中的新 'feature' 还是一个讨厌的错误?
不幸的是,我不知道这是否被视为一项功能(我不这么认为)。如果任何 class subclass 是符合 Equatable
的 class(如 NSObject
;它比较实际实例),则会出现此问题。因此,如果您仅 "override" subclass 的 ==
运算符,则所有其他运算符如:
func !=<T : Equatable>(lhs: T, rhs: T) -> Bool
func ==<T : Equatable>(lhs: T?, rhs: T?) -> Bool
func ==<T : Equatable>(lhs: [T], rhs: [T]) -> Bool
其中 T
被限制为 Equatable
Swift 使用基数 class 的 ==
运算符。作为(耗时的)解决方法,您可以重载所有必须使用的相等运算符,如下所示:
func !=(lhs: MyObject, rhs: MyObject) -> Bool { ... }
func ==(lhs: MyObject?, rhs: MyObject?) -> Bool { ... }
func ==(lhs: [MyObject], rhs: [MyObject]) -> Bool { ... }
编辑:原因
这种行为的原因是,如果一个子class符合Equatable
,则自我要求的Self
被确定为这个class。因此,每次使用符合 Equatable
的(通用)类型调用 ==
时,它只会调用初始符合 class.
我认为这种行为应该被认为是一个错误(从 Xcode 7 beta 6 开始仍然存在),但有一个临时的解决方法:重写 NSObject 的 -isEqual
而不是实现 Swift 的 ==
运算符。
class MyObject: NSObject {
let identifier: String
init(identifier: String) {
self.identifier = identifier
}
override func isEqual(object: AnyObject?) -> Bool {
guard let rhs = object as? MyObject else {
return false
}
let lhs = self
return lhs.identifier == rhs.identifier
}
}
我在此处找到了该问题的另一个参考资料,其中包含更多代码示例:http://mgrebenets.github.io/swift/2015/06/21/equatable-nsobject-with-swift-2/
NSManagedObject
(explained here),所以我创建了一个新协议 NSObjectSubclassEquatable
,您可以使用它来比较 NSobject
子类。
infix operator =~= {}
public protocol NSObjectSubclassEquatable {
static func compare(lhs: Self,_ rhs: Self) -> Bool
}
public func =~=<T : NSObjectSubclassEquatable>(lhs: T, rhs: T) -> Bool {
return T.compare(lhs, rhs)
}
func =~=<Element : NSObjectSubclassEquatable>(lhs: [Element], rhs: [Element]) -> Bool {
for (lhsElement,rhsElement) in zip(lhs, rhs) {
if !(lhsElement =~= rhsElement) {
return false
}
}
return true
}
示例:
class Point: NSObject {
var x: Int
var y: Int
init(_ x: Int,_ y: Int) {
self.x = x
self.y = y
}
}
extension Point: NSObjectSubclassEquatable {
static func compare(lhs: Point,_ rhs: Point) -> Bool {
return lhs.x == rhs.x && lhs.y == rhs.y
}
}
Point(1,2) =~= Point(1,2) // Prints true
[Point(1,2)] =~= [Point(1,2)] // Prints true