Ошибка с оператором equals и NSObjects в Swift 2.0?
Хорошо, что-то странное происходит при написании собственного оператора equals для подклассов NSObject в Swift 2.0 следующим образом:
func ==(lhs: MyObject, rhs: MyObject) -> Bool {
return lhs.identifier == rhs.identifier
}
Для класса, который выглядит так:
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
Таким образом, очевидно,!= больше не вызывает оператор == и отрицает его. Кажется, что просто сравнивайте экземпляры при использовании!=
Все это происходит только тогда, когда ваш класс является подклассом NSObject (прямо или косвенно). Когда это не так, все работает так, как вы ожидали.
Может ли кто-нибудь сказать мне, является ли это новой "функцией" в Swift 2.0 или просто неприятной ошибкой?
Ответы
Ответ 1
К сожалению, я не знаю, считается ли это признаком или нет (я так не думаю). Эта проблема возникает, если какой-либо класс подклассы класса, который соответствует Equatable
(например, NSObject
; он сравнивает фактические экземпляры). Поэтому, если вы только "переопределите" оператор ==
подкласса, все остальные операторы, такие как:
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 использует оператор ==
базового класса. В качестве временного решения вы можете перегрузить все операторы равенства, которые вы должны использовать так:
func !=(lhs: MyObject, rhs: MyObject) -> Bool { ... }
func ==(lhs: MyObject?, rhs: MyObject?) -> Bool { ... }
func ==(lhs: [MyObject], rhs: [MyObject]) -> Bool { ... }
Изменить: Причина
Причиной такого поведения является то, что если подкласс соответствует Equatable
, то Self
для самостоятельного требования определяется как этот класс. Поэтому каждый раз, когда ==
вызывается с (общим) типом, который соответствует Equatable
, он вызывает только оператор исходного соответствующего класса.
Ответ 2
Я думаю, что это поведение следует считать ошибкой (все еще присутствующей на 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/
Ответ 3
ответ kylealanhale не работает с NSManagedObject
(пояснил здесь), поэтому я создал новый протокол 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