[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