We have multiple instances of a custom Swift class, which inherits from SKSpriteNode, and were able to execute the following code (grossly simplified for this question) correctly:
let instance1 = CustomClass()
let instance2 = CustomClass()
let instance3 = CustomClass()
let instance4 = CustomClass()
let array1 = [instance1, instance2]
let array2 = [instance3, instance4]
func check(testInstance: CustomClass) -> Bool {
return array1.filter({ $0 == testInstance }).count > 0
}
check(testInstance: instance3)
In other words, executing check(testInstance: instance3)
returned false
as expected.
However, we made a bunch of changes, and check
stopped working.
CustomClass
does not implement the Equatable
protocol. We just want to detect unique instances.
It only started working when we used ObjectIdentifier
, meaning the function changed to this:
func check(testInstance: CustomClass) -> Bool {
return array1.filter({ ObjectIdentifier($0) == ObjectIdentifier(testInstance) }).count > 0
}
Why is ObjectIdentifier
needed, and when should it be used for object equality?
This was written with Swift 3.
You don't need to use
ObjectIdentifier
in order to perform an identity comparison in this case, you can simply use the identity operator===
instead which, as Martin says here, for class instances is equivalent to usingObjectIdentifier
's==
overload:Also note we're using
contains(where:)
overfilter{...}.count > 0
, as the former will short-circuit upon finding a matching element, whereas the latter evaluates the entire sequence (and creates an unnecessary intermediate array).The direct use of
==
to perform an identity comparison of objects may have worked due to the fact thatCustomClass
ultimately inherits fromNSObject
, which conforms toEquatable
by defining an==
overload that callsisEqual(_:)
, which by default performs an identity comparison.However in general, this should not be relied upon – the implementation of
isEqual(_:)
can be overridden to perform a comparison based on property values rather than identity. Furthermore, semanticallyEquatable
requires that the implementation of==
is based all on visible aspects (i.e property values) of the instances being compared.From the documentation:
Therefore using
==
for an identity comparison on your class was never correct, even though it may have worked initially.As for when
ObjectIdentifier
should be used, really you should never need it just to perform an identity comparison. For classes, you should use the===
operator, and for metatypes, you should simply use the==
overload defined specifically for them (as in that case, identity just happens to equality, as each new metatype instance is unique).The main use of
ObjectIdentifier
is really itshashValue
implementation, which is derived from the pointer value of the thing that it's initialised with. This can be useful, for example, in allowing metatypes to beDictionary
keys (compare Make a Swift dictionary where the key is "Type"?).