Bug with equals operator and NSObjects in Swift 2.0?

心已入冬 提交于 2019-11-29 01:57:57

Unfortunately I don't know whether this is considered a feature or not (I don't think so). This problem occurs if any class subclasses a class which conforms to Equatable(like NSObject; it compares actual instances). So if you only "override" the == operator of the subclass all other operators like:

func !=<T : Equatable>(lhs: T, rhs: T) -> Bool
func ==<T : Equatable>(lhs: T?, rhs: T?) -> Bool
func ==<T : Equatable>(lhs: [T], rhs: [T]) -> Bool

where T is constrained to be Equatable Swift uses the == operator of the baseclass. As (time-consuming) workaround you can overload all the equality operators you have to use like so:

func !=(lhs: MyObject, rhs: MyObject) -> Bool { ... }
func ==(lhs: MyObject?, rhs: MyObject?) -> Bool { ... }
func ==(lhs: [MyObject], rhs: [MyObject]) -> Bool { ... }

Edit: The Reason

The reason for this behavior is that if a subclass conforms to Equatable the Self of the self requirement is determined to be this class. So every time the == is called with a (generic) type that conforms to Equatable it only calls the operator of the initial conforming class.

I think this behavior should be considered a bug (still present as of Xcode 7 beta 6), but there's a hopefully temporary workaround: override NSObject's -isEqual instead of implementing Swift's == operator.

class MyObject: NSObject {
    let identifier: String
    init(identifier: String) {
        self.identifier = identifier
    }
    override func isEqual(object: AnyObject?) -> Bool {
        guard let rhs = object as? MyObject else {
            return false
        }
        let lhs = self

        return lhs.identifier == rhs.identifier
    }
}

I found another reference to the problem, with more code examples, here: http://mgrebenets.github.io/swift/2015/06/21/equatable-nsobject-with-swift-2/

david-hoze

kylealanhale's answer does not work with NSManagedObject (explained here), so I created a new protocol NSObjectSubclassEquatable that you can use for comparing NSobject subclasses.

infix operator =~= {}

public protocol NSObjectSubclassEquatable {

  static func compare(lhs: Self,_ rhs: Self) -> Bool
}


public func =~=<T : NSObjectSubclassEquatable>(lhs: T, rhs: T) -> Bool {

  return T.compare(lhs, rhs)
}

func =~=<Element : NSObjectSubclassEquatable>(lhs: [Element], rhs: [Element]) -> Bool {
  for (lhsElement,rhsElement) in zip(lhs, rhs) {
    if !(lhsElement =~= rhsElement) {
      return false
    }
  }
  return true
}

Example:

class Point: NSObject {

  var x: Int
  var y: Int

  init(_ x: Int,_ y: Int) {
    self.x = x
    self.y = y
  }
}

extension Point: NSObjectSubclassEquatable {

  static func compare(lhs: Point,_ rhs: Point) -> Bool {
    return lhs.x == rhs.x && lhs.y == rhs.y
  }
}

Point(1,2) =~= Point(1,2) // Prints true
[Point(1,2)] =~= [Point(1,2)] // Prints true
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!