Uploaded image for project: 'Swift'
  1. Swift
  2. SR-9331

NSMutableSet uses identity check instead of equality upon adding Swift class

    XMLWordPrintable

    Details

    • Type: Bug
    • Status: Open
    • Priority: Medium
    • Resolution: Unresolved
    • Component/s: Standard Library
    • Labels:
    • Environment:

      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
      

       

        Attachments

          Activity

            People

            Assignee:
            Unassigned Unassigned
            Reporter:
            ifndefgt Gokhan Topcu
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Dates

              Created:
              Updated: