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

Zhao Xin owenzx at gmail.com
Wed Mar 29 04:36:58 CDT 2017


Now I understand it. What I don't understand is why there is nowhere metioning this. All docs are talking about Hashable, clearly that NSObject didn't rely on it.

Zhaoxin

Get Outlook for iOS<https://aka.ms/o0ukef>
________________________________
From: Saagar Jha <saagar at saagarjha.com>
Sent: Wednesday, March 29, 2017 11:47:51 AM
To: Zhao Xin
Cc: Swift Users List
Subject: Re: [swift-users] Set with element of NSObject subclasses didn't work as expected

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<mailto: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<mailto: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/20170329/73903022/attachment.html>


More information about the swift-users mailing list