Search code examples
iosswiftcore-imageciimageequatable

Should CIImage be Equatable?


So, Apple’s documentation says that CIImage conforms to Equatable. I would take this to mean that the following unit test would pass. However, it doesn’t. I’m interested in why.

func test_CIImageEqualityShouldWork() {
    let bundle = NSBundle(forClass: PrototypeTests.self)
    guard let path = bundle.pathForResource("testImage", ofType: "png") else { return }
    guard let image = UIImage(contentsOfFile: path) else { return }

    let thingy1 = CIImage(image: image)
    let thingy2 = CIImage(image: image)
    XCTAssert(thingy1 == thingy2)
}

The image exists, the guard statements both pass, but the assert fails, they aren’t equal.

Out of interest, I’ve tried creating the UIImage twice and comparing those too. That also fails.


Solution

  • All NSObject subclasses conform to Equatable, and the == function calls the isEqual: method on the objects. The isEqual: method of NSObject simply compares the object pointers, i.e. o1 == o2 holds if o1 and o2 refer the same object instance.

    See for example Interacting with Objective-C APIs:

    Swift provides default implementations of the == and === operators and adopts the Equatable protocol for objects that derive from the NSObject class. The default implementation of the == operator invokes the isEqual: method, and the default implementation of the === operator checks pointer equality. You should not override the equality or identity operators for types imported from Objective-C.

    The base implementation of the isEqual: provided by the NSObject class is equivalent to an identity check by pointer equality.

    Many NSObject subclasses override the isEqual: method (e.g. NSString, NSArray, NSDate, ...) but not CIImage:

    let thingy1 = CIImage(image: image)
    let thingy2 = CIImage(image: image)
    

    creates two different CIImage instances and these compare as "not equal".