2015-09-22 7 views
6

Po aktualizacji naszego kodu do Swift2 napotkałem nietypowy problem. Zestaw nie jest odejmowany ani zrzeszany zgodnie z oczekiwaniami.Zestaw Swift 2.0 nie działa zgodnie z oczekiwaniami, gdy zawiera podklasę NSObject

class A: NSObject { 
    let h: Int 

    init(h: Int) { 
     self.h = h 
    } 

    override var hashValue: Int { 
     return h 
    } 
} 

func ==(lhs: A, rhs: A) -> Bool { 
    return lhs.hashValue == rhs.hashValue 
} 

let a = A(h: 1) 
let b = A(h: 1) 

var sa = Set([a]) 
let sb = Set([b]) 

sa.subtract(sb).count // Swift1.2 prints 0, Swift 2 prints 1 

sa.contains(a) // Swift1.2 true, Swift 2 true 
sa.contains(b) // Swift1.2 true, Swift 2 false 

Wygląda na to, że nowy zestaw nie używa hashValue do wewnętrznych operacji. Czy jest jakiś błąd lub sposób obejścia tego problemu?

Odpowiedz

11

Grałem trochę z twoim kodem. Udało mi się dostać pracy przez nie instacji NSObject, lecz zgodnych z Hashable protokołem:

class A: Hashable { 
    let h: Int 

    init(h: Int) { 
     self.h = h 
    } 

    var hashValue: Int { 
     return h 
    } 

} 

func ==(lhs: A, rhs: A) -> Bool { 
    return lhs.hashValue == rhs.hashValue 
} 

let a = A(h: 1) 
let b = A(h: 1) 

var sa = Set([a]) 
let sb = Set([b]) 

sa.subtract(sb).count // Swift1.2 prints 0, Swift 2 prints 1 

sa.contains(a) // Swift1.2 true, Swift 2 true 
sa.contains(b) // Swift1.2 true, Swift 2 false 

a.hashValue == b.hashValue 

kiedy byłaś dziedziczy z NSObject, twój przeciążenie == nie była faktycznie wykonywana. Jeśli chcesz, to do pracy z NSObject, trzeba by zastąpić isEquals:

override func isEqual(object: AnyObject?) -> Bool { 
    if let object = object as? A { 
     return object.h == self.h 
    } else { 
     return false 
    } 
} 
+0

Dziękujemy! Miałem ten problem z podklasą MKAnnotation (która również musi rozszerzyć NSObject). Czy masz link do jakiejś dokumentacji na ten temat? – brki

+1

Zarówno isEqual, jak i hashValue wydają się być potrzebne do poprawnego ustawienia zestawu z NSObject w Swift 3 –

2
//: Playground - noun: a place where people can play 

import Foundation 

class X: NSObject { 
    var x:String 
    var y:Int 

    init(x:String, y:Int) { 
     self.x = x 
     self.y = y 
     super.init() 
    } 

    override var hashValue: Int { 
     return x.hashValue^y.hashValue 
    } 

    override func isEqual(object: AnyObject?) -> Bool { 
     if let rhs = object as? X { 
      return x == rhs.x && y == rhs.y 
     } else { 
      return false 
     } 
    } 
} 

func == (lhs:X, rhs:X) -> Bool { 
    return lhs.isEqual(rhs) 
} 

let x = X(x: "x1", y: 1) 
let y = X(x: "x1", y: 1) 
X(x: "x1", y: 1) == X(x: "x1", y: 1) // Swift 'x == y' (true) 
x.isEqual(y)       // Obj-C '[x isEqual: y]' (true) 

var s:Set<X> = [X(x: "x1", y: 1)] 
s.count // count == 1 
s.insert(X(x: "x2", y: 1)) 
s.count // count == 2 
s.insert(X(x: "x1", y: 1)) 
s.count // count == 2 
s.insert(X(x: "x2", y: 1)) 
s.count // count == 2 

spędziłem czas szukając właściwej odpowiedzi na to aż uderzę to Pytanie/odpowiedź. Powróciłem do podstaw w XCode Playground, aby zobaczyć, co się dzieje. Korzystanie z podklas NSObject w Swift Set tworzy garść bardziej czytelnego kodu.

+0

Nie wiem, dlaczego tak wiele osób wybrało inną odpowiedź. Dla mnie ten jest wartościowy i poprawny. Dziękuję Ci bardzo. –

+0

Trzeba tylko wspomnieć, że jest to wymagane w 'NSObjectProtocol', jeśli przesłonisz' func isEqual (_ object: Any?) -> Bool', musisz również przesłonić 'var hash: Int {get}'. Tak jak w dokumentach, mówi: "Jeśli dwa obiekty są równe (zgodnie z metodą isEqual (_ :)), muszą mieć tę samą wartość skrótu. '. https://developer.apple.com/reference/objectivec/nsobjectprotocol/1418859-hash –

Powiązane problemy