Reputation: 5082
As far as I understand, identity operator is used to determine if two objects have the same reference. It means, in practice, the both side of the operator shall be an object.
However, I've tried the following code and it confused me of the identity operator's function of what I understand
class Dog {}
let d: Dog = Dog()
if type(of: d) === Dog.self {
print("yep") //prints out "yep"
}
if type(of: d) == Dog.self {
print("yep") //prints out "yep"
}
The left and right side of the identity operator is not an object but a type and it seems, for this point, semantic equivalence operator and object identity operator (looks like) works in the same way.
Question:
Is this a bug or I didn't get the whole point correctly.
Thanks for your help and time
Upvotes: 2
Views: 155
Reputation: 80781
The left and right side of the identity operator is not an object but a type.
Actually, on Apple platforms, they are objects.
This is due to the fact that Swift classes are implemented as Objective-C classes under the hood, as Mike Ash goes into detail in this great blog post. This means that the metatype of a class is also an Objective-C class, and therefore conforms to AnyObject
.
Because of this, you can compare class metatypes with the identity operator, as it's defined as:
public func ===(lhs: AnyObject?, rhs: AnyObject?) -> Bool
It'll compare whether the two objects are the same object, or specifically in this case, the same class metatype.
In contrast, under the hood, the metatype for a value type isn't an Objective-C object – it's just a pointer to some static metadata. If we rewrite your example to use a struct
:
struct Dog {}
let d = Dog()
// Binary operator '===' cannot be applied to two 'Dog.Type' operands
if type(of: d) === Dog.self {
print("yep")
}
You'll see that we can no longer use ===
to compare metatypes, as they don't conform to AnyObject
. So really, the ability to use the identity operator to compare class metatypes is just a side-effect of them being implemented as Objective-C objects.
The universal way of comparing metatypes is with the equality operator ==
, as Swift provides an overload specifically for metatypes:
public func ==(t0: Any.Type?, t1: Any.Type?) -> Bool
This checks whether two metatypes are the same, however unlike ===
, it works with both class metatypes and value-typed metatypes. Under the hood, it's implemented as a simple pointer comparison, so should always yield identical results as ===
with class metatypes.
I would therefore always recommend comparing metatypes with ==
, as you're not relying on the conformance to AnyObject
. For example, on a Linux platform, class metatypes don't conform to AnyObject
and therefore cannot be compared with the identity operator (although interestingly, when Foundation
is imported, it appears to add a ===
overload for AnyObject.Type
operands – presumably to aid interoperability).
Upvotes: 1
Reputation: 462
Not a bug, they are the same thing.
Dog, as a class (type) is a singular, there can only be one. There can be many Instances but only one of the Class.
type(of: d) return the owner Class of d, Dog.self returns the Class itself. They are exactly the same object, the singular Class of Dog.
Upvotes: 1