I was surprised to find that the following code produces a warning:
func foo<T>(_ t: T) -> ObjectIdentifier? {
if let r = t as? AnyObject {
return ObjectIdentifier(r)
} else {
return nil
}
}
Warning: Conditional cast from 'T' to 'AnyObject' always succeeds
I explored a bit more by running this function with integers, and it seems that each unique value corresponds to a distinct object identifier:
let a = 7
print(fooo(a)!) // ObjectIdentifier(0x89815300d8f91468)
print(fooo(a)!) // ObjectIdentifier(0x89815300d8f91468)
print(fooo(7)!) // ObjectIdentifier(0x89815300d8f91468)
print(fooo(8)!) // ObjectIdentifier(0x89815300d8f913e8)
var b = 8
print(fooo(b)!) // ObjectIdentifier(0x89815300d8f913e8)
b += 1
print(fooo(b)!) // ObjectIdentifier(0x89815300d8f913e8)
Why do we allow casting any value to AnyObject when it is not the case that every value "conforms" to it?
I put "conforms" in quotes because I'm aware that AnyObject is not a normal protocol, rather a "layout constraint" or something like that.
I'm assuming that this special nature of AnyObject is related to why the conditional cast always succeeds, but I would love to read about the technical details in more depth.
Casting to AnyObject succeeds by design; the point of AnyObject is to represent a reference to some reference type. If you already have a reference type (i.e. a class instance), then that already "conforms" to AnyObject anyway, but if you have a value type, then casting to AnyObject is really an instruction to the Swift compiler to box it in a reference type for you.
You can see this in the REPL; on Linux it looks like this:
1> struct Foo {}
2> Foo() as AnyObject
$R0: __SwiftValue = {
value = {}
}
while on Darwin (where we have ObjC interop), it instead looks like
1> struct Foo {}
2> Foo() as AnyObject
$R0: __SwiftValue = {
baseNSObject@0 = {
isa = __SwiftValue
}
}
It's probably most useful when passing Swift things through ObjC code; for instance, various ObjC APIs provide the ability to pass through an arbitrary id to some callback, and in that case you can use as AnyObject to sneak a Swift struct through without having to change it into an @objc class.