After upgrading our codebase to Swift2 I've encountered unusual problem. Set is not substracting nor unioning as expected.
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
It looks like new Set is not using hashValue for internal operations. Any idea is that a bug, or a way to workaround this issue?
I played with your code a bit. I was able to get it working by no longer subclassing NSObject, but instead conforming to the Hashable protocol:
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
When you were inheriting from NSObject, your ==
overload wasn't actually being executed. If you want this to work with NSObject, you'd have to override isEquals:
override func isEqual(object: AnyObject?) -> Bool {
if let object = object as? A {
return object.h == self.h
} else {
return false
}
}
//: 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
I spent time looking for the right answer to this until I hit this Question/Answer. I had reverted to basics in XCode Playground to see what was happening. Using subclasses of NSObject
in Swift Set
makes a bunch of more readable code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With