2015-06-28 18 views
11

Ok, qualcosa di strano sta accadendo quando si scrive il proprio uguale operatore sottoclassi NSObject a Swift 2.0 in questo modo:Bug con operatore uguale e NSObjects in Swift 2.0?

func ==(lhs: MyObject, rhs: MyObject) -> Bool { 
    return lhs.identifier == rhs.identifier 
} 

Per una classe che assomiglia a questo:

class MyObject: NSObject { 
    let identifier: String 
    init(identifier: String) { 
     self.identifier = identifier 
    } 
} 

Ciò usata per funzionare solo bene in Swift 1.2 e sotto. E 'ancora un po' funziona:

let myObject1 = MyObject(identifier: "A") 
let myObject2 = MyObject(identifier: "A") 
let result = (myObject1 == myObject2) 
// result is true 

Fin qui tutto bene, ma se entrambe le variabili sono state optional?

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 

E un'altra cosa che non funziona più:

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 

Quindi, apparentemente, = non chiama più l'operatore == e nega. Sembra semplicemente confrontare le istanze invece quando si utilizza! =

Tutto questo accade solo quando la classe è una sottoclasse di NSObject (direttamente o indirettamente). Quando non lo è, tutto funziona esattamente come ti aspetteresti.

Qualcuno può dirmi se questa è una nuova "funzionalità" in Swift 2.0 o solo un brutto bug?

+0

Cercavi di scrivere '==', invece di = ', nella funzione di' '=='? Potrebbe essere la causa dello strano comportamento, ma non l'ho ancora testato. – ABakerSmith

+0

Purtroppo questa non è la causa (vedi la mia risposta sotto) ma hai ragione dovrebbe essere '==' – Qbyte

+0

Spiacente, errore di battitura. Ma quello non era il mio problema. –

risposta

9

Purtroppo non so se questo è considerato una caratteristica o meno (non credo). Questo problema si verifica se una classe sottoclasse una classe conforme a Equatable (come NSObject, confronta le istanze effettive). Quindi, se solo "override" la == operatore della sottoclasse di tutti gli altri operatori come:

func !=<T : Equatable>(lhs: T, rhs: T) -> Bool 
func ==<T : Equatable>(lhs: T?, rhs: T?) -> Bool 
func ==<T : Equatable>(lhs: [T], rhs: [T]) -> Bool 

dove T è costretto a essere Equatable Swift utilizza il == operatore del baseclass. Come (in termini di tempo) Soluzione alternativa è possibile sovraccaricare tutti gli operatori di uguaglianza si deve utilizzare in questo modo:

func !=(lhs: MyObject, rhs: MyObject) -> Bool { ... } 
func ==(lhs: MyObject?, rhs: MyObject?) -> Bool { ... } 
func ==(lhs: [MyObject], rhs: [MyObject]) -> Bool { ... } 

Edit: La ragione

La ragione di questo comportamento è che se una sottoclasse è conforme a Equatable il self-service è determinato per essere questa classe. Pertanto, ogni volta che lo == viene chiamato con un tipo (generico) conforme a Equatable, chiama solo l'operatore della classe conforme iniziale.

+0

Non risponde alla tua domanda? – Qbyte

+2

Questo è ancora presente in Xcode 7 GM. Hai mai scoperto se si tratta di un cambiamento intenzionale? – Bill

+0

@Bill Ho scoperto che questo comportamento esiste anche in Swift 1.2 e ho modificato la mia risposta (motivo di questo comportamento). – Qbyte

10

Penso che questo comportamento debba essere considerato un bug (ancora presente a partire da Xcode 7 beta 6), ma c'è una soluzione temporanea speranzosa: ignorare NSObject -isEqual invece di implementare l'operatore di 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 
    } 
} 

ho trovato un altro riferimento al problema, con ulteriori esempi di codice, qui: http://mgrebenets.github.io/swift/2015/06/21/equatable-nsobject-with-swift-2/

+0

Anche in versione Xcode 7! –

0

kylealanhale's answer non funziona con NSManagedObject (spiegato here), così ho creato un nuovo protocollo NSObjectSubclassEquatable che è possibile utilizzare per confrontare sottoclassi 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 
} 

Esempio:

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