Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SR-9331] NSMutableSet uses identity check instead of equality upon adding Swift class #51801

Open
swift-ci opened this issue Nov 24, 2018 · 2 comments

Comments

@swift-ci
Copy link
Collaborator

@swift-ci swift-ci commented Nov 24, 2018

Previous ID SR-9331
Radar None
Original Reporter ifndefgt (JIRA User)
Type Bug
Environment

Checked with both

Apple Swift version 4.0.3 (swiftlang-900.0.74.1 clang-900.0.39.2)

Target: x86_64-apple-macosx10.9

and

Apple Swift version 4.2.1 (swiftlang-1000.11.42 clang-1000.11.45.1)

Target: x86_64-apple-darwin17.7.0

Additional Detail from JIRA
Votes 0
Component/s Standard Library
Labels Bug, Runtime
Assignee None
Priority Medium

md5: ff3f6afba9d8cb7ef2090342fe90af70

Issue Description:

I have a Swift class below. When I insert its 2 equal instances into a Swift Set upon insertion Hashable protocol implementation is used as expected. However when I insert the same instances into NSMutableSet none of the implementations (Hashable or NSObjectProtocol methods) are used.

When I disassemble the code, as I see _SwiftObject_hash method is called. But it just returns object address, so identity is used for duplicate checking.

When I change the class type to a Swift struct or a class that inherits NSObject everything works as expected.

I'm not sure if this is expected behavior. But if it's that's really confusing. It caused a bug in my project and cost me one day of debugging, trying alternatives etc. After searching for a while I couldn't find any resource referencing this behavior, so here is a bug report.

class Model: Hashable {
    let name: String
    let number: Int
 
    init(name: String, number: Int) {
        self.name = name
        self.number = number
    }
 
    static func ===(lhs: Model, rhs: Model) -> Bool {
        return (lhs.name == rhs.name)
            && (lhs.number == rhs.number)
    }
 
     static func ==(lhs: Model, rhs: Model) -> Bool {
        return (lhs.name == rhs.name)
            && (lhs.number == rhs.number)
    }
 
    var hashValue: Int {
        return name.hashValue << 1 &+ number
    }
 
    var hash: Int {
        return name.hashValue << 1 &+ number
    }
 
    func isEqual(_ object: Any?) -> Bool {
        guard let object = object as? Model else {
            return false
        }
        return (self.name == object.name)
            && (self.number == object.number)
    }
 
    func isEqual(_ object: AnyObject?) -> Bool {
        guard let object = object as? Model else {
            return false
        }
        return (self.name == object.name)
            && (self.number == object.number)
    }
}

Usage:

var swiftSet = Set<Model>()
var nextStepSet = NSMutableSet()
let name = "Gokhan Topcu"
let model1 = Model(name: name, number: name.count)
let model2 = Model(name: name, number: name.count)
swiftSet.insert(model1)
swiftSet.insert(model2)
print(swiftSet.count) // prints 1
 
nextStepSet.add(model1)
nextStepSet.add(model2)
print(nextStepSet.count) // prints 2
@belkadan
Copy link
Contributor

@belkadan belkadan commented Nov 26, 2018

cc @jckarter, @mikeash. I think we just never finished the SwiftObject/Hashable implementation…

@mikeash
Copy link
Contributor

@mikeash mikeash commented Nov 26, 2018

- (NSUInteger)hash {
return (NSUInteger)self;
}

- (BOOL)isEqual:(id)object {
return self == object;
}

Yeah....

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants