[swift-users] Set with element of NSObject subclasses didn't work as expected
Joe Groff
jgroff at apple.com
Wed Mar 29 13:36:44 CDT 2017
> On Mar 28, 2017, at 12:50 PM, Zhao Xin via swift-users <swift-users at swift.org> wrote:
>
> Please see the code first.
>
> import Foundation
>
> class Foo:Hashable {
> let value:Int
>
> public var hashValue: Int { return value }
> public static func ==(lhs: Foo, rhs: Foo) -> Bool {
> return lhs.value == rhs.value
> }
>
> init(_ value:Int) {
> self.value = value
> }
> }
>
> let fooSetA:Set = [Foo(8), Foo(9)]
> let fooSetB:Set = [Foo(9), Foo(10)]
> let fooResultC = fooSetA.intersection(fooSetB) // {{value 9}}
> let fooResultD = fooSetA.subtracting(fooSetB) // {{value 8}}
>
>
> class Bar:NSObject {
> let value:Int
>
> override public var hashValue: Int { return value }
> public static func ==(lhs: Bar, rhs: Bar) -> Bool {
> return lhs.value == rhs.value
> }
>
> init(_ value:Int) {
> self.value = value
> }
> }
>
> let barSetA:Set = [Bar(8), Bar(9)]
> let barSetB:Set = [Bar(9), Bar(10)]
> let barResultC = barSetA.intersection(barSetB) // Set([])
> let barResultD = barSetA.subtracting(barSetB) // {{NSObject, value 9}, {NSObject, value 8}}
>
>
> Behaviors of `func intersection(Set<Set.Element>)` and `func subtracting(Set<Set.Element>)` were different between normal Swift class and NSObject subclasses. I had thought they should be the same. It seemed that Set<NSObject> relied on addresses of NSObject instances instead of their hashValues. That made the Set useless.
This is a known issue—when you redeclare `static func ==` inside Bar, that does not override `NSObject.==`, and moreover *cannot*, since overriding `NSObject.==` requires an implementation that accepts two NSObjects. We ought to consider this an error, or at least a warning. NSObject conforms to Equatable on behalf of all its subclasses by implementing `==` in terms of `isEqual:` and `hashValue` in terms of `hash`. Even though you can override `hashValue` for Swift, it's probably still safer to override `hash` and `isEqual` in order to get common behavior between ObjC and Swift, since ObjC will not use Swift's conformances. Perhaps the NSObject implementation of `hashValue` should be final to help with this.
-Joe
More information about the swift-users
mailing list