Let's say I'm implementing a root class in Swift, which I declare adopts the Equatable
protocol (I want to be able to tell if an array of my type contains a given instance or not).
What is the difference -if any, in this specific case- between implementing the protocol's required ==
operator as:
public static func ==(lhs: MyClass, rhs: MyClass) -> Bool {
return ObjectIdentifier(lhs) == ObjectIdentifier(rhs)
}
...as opposed to just doing this:
public static func ==(lhs: MyClass, rhs: MyClass) -> Bool {
return (lhs === rhs)
}
As a reference, this is what the documentation says about ObjectIdentifier()
:
A unique identifier for a class instance or metatype. In Swift, only class instances and metatypes have unique identities. There is no notion of identity for structs, enums, functions, or tuples.
...and this is what the "Basic Operators" section of The Swift Programming Language (Swift 3) says about the ===
operator:
NOTE
Swift also provides two identity operators (
===
and!==
), which you use to test whether two object references both refer to the same object instance. For more information, see Classes and Structures.
There is no difference for class instance, see the following comments in ObjectIdentifier.swift:
/// Creates an instance that uniquely identifies the given class instance.
///
/// The following example creates an example class `A` and compares instances
/// of the class using their object identifiers and the identical-to
/// operator (`===`):
///
/// class IntegerRef {
/// let value: Int
/// init(_ value: Int) {
/// self.value = value
/// }
/// }
///
/// let x = IntegerRef(10)
/// let y = x
///
/// print(ObjectIdentifier(x) == ObjectIdentifier(y))
/// // Prints "true"
/// print(x === y)
/// // Prints "true"
///
/// let z = IntegerRef(10)
/// print(ObjectIdentifier(x) == ObjectIdentifier(z))
/// // Prints "false"
/// print(x === z)
/// // Prints "false"
///
It also becomes apparent from the
implementation of ==
for ObjectIdentifier
,
which just compares the pointers to the object storage:
public static func == (x: ObjectIdentifier, y: ObjectIdentifier) -> Bool {
return Bool(Builtin.cmp_eq_RawPointer(x._value, y._value))
}
which is what the ===
operator
does as well:
public func === (lhs: AnyObject?, rhs: AnyObject?) -> Bool {
switch (lhs, rhs) {
case let (l?, r?):
return Bool(Builtin.cmp_eq_RawPointer(
Builtin.bridgeToRawPointer(Builtin.castToUnknownObject(l)),
Builtin.bridgeToRawPointer(Builtin.castToUnknownObject(r))
))
case (nil, nil):
return true
default:
return false
}
}
ObjectIdentifier
conforms to Hashable
, so it is useful if you want to implement that protocol for your class:
extension MyClass: Hashable {
var hashValue: Int {
return ObjectIdentifier(self).hashValue
}
}
An object identifier can also be created for meta types
(e.g. ObjectIdentifier(Float.self)
) for which ===
is not defined.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With