[swift-users] Set with element of NSObject subclasses didn't work as expected

Saagar Jha saagar at saagarjha.com
Tue Mar 28 22:47:51 CDT 2017


NSObject’s hash (which Set uses) is done via ObjectIdentifier, which IIRC uses something along the lines of the object's address. That’s why you’re getting the behavior you’re seeing.

Saagar Jha

> On Mar 28, 2017, at 20:20, Zhao Xin via swift-users <swift-users at swift.org> wrote:
> 
> Turns out that for `NSObject`, protocol `Equatable` wasn't used. Instead, it used `NSObjectProtocol.isEqual(_ object: Any?)`. Also, override `func isEqual(_ object: Any?) -> Bool` requires to override `var hash: Int { get }` as well.
> 
> I think this behavior should be mentioned in Swift docs or manual in `Set` section.
> 
> Below code works. 
> 
> 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
>     }
>     // required by NSObjectProtocol
>     override func isEqual(_ object: Any?) -> Bool {
>         if let rhs = object as? Bar {
>             return self == rhs
>         }
>         return false
>     }
>     override var hash: Int { return self.hashValue }
>     
>     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) // {{NSObject, value 9}}
> let barResultD = barSetA.subtracting(barSetB) // {{NSObject, value 8}}
> 
> Gladly I find it here <http://stackoverflow.com/questions/32726524/swift-2-0-set-not-working-as-expected-when-containing-nsobject-subclass>. 
> 
> Zhaoxin
> 
> 
> 
> On Wed, Mar 29, 2017 at 3:50 AM, Zhao Xin <owenzx at gmail.com <mailto:owenzx at gmail.com>> 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.
> 
> Swift version: 3.1 (swiftlang-802.0.48 clang-802.0.48)
> Xcode 8.3 (8E162)
> 
> Zhaoxin
> 
> 
> 
> 
> 
> 
> 
> 
> 
> 
> 
> _______________________________________________
> swift-users mailing list
> swift-users at swift.org
> https://lists.swift.org/mailman/listinfo/swift-users

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-users/attachments/20170328/95a9cdb9/attachment.html>


More information about the swift-users mailing list