It took me a while to wrap my head around this, so I think the code will show the situation better:
@objc(OBJCProto) protocol Proto {}
let objcProtocol = NSProtocolFromString("OBJCProto")
ObjectIdentifier(Proto.self) == ObjectIdentifier(objcProtocol!) // false
ObjectIdentifier(Proto.self as Any.Type) == ObjectIdentifier(objcProtocol!) // false
ObjectIdentifier(Proto.self as AnyObject) == ObjectIdentifier(objcProtocol!) // true
ObjectIdentifier(Proto.self as Protocol) == ObjectIdentifier(objcProtocol!) // true
NSStringFromProtocol(objcProtocol!) // OBJCProto
NSStringFromProtocol(Proto.self) // OBJCProto
String(reflecting: Proto.self) // __C.OBJCProto
String(reflecting: objcProtocol) // <Protocol 0x...>
String(reflecting: Proto.self) // __C.OBJCProto
String(reflecting: Proto.self as Any.Type) // __C.OBJCProto
String(reflecting: Proto.self as AnyObject) // <Protocol 0x...>
String(reflecting: Proto.self as Protocol) // <Protocol 0x...>
Metatypes are treated as different from protocols, unless the bridging kicks in when you use it as an AnyObject. This inconsistency caused me issues when I attempted to write a method that converted a metatype (or obj-c protocol) to a string using reflection, because the place the type was coming from was altering the result:
func identifier(forBridgedProtocol proto: Any) -> String {
if let objcProtocol = object as? Protocol {
return NSStringFromProtocol(objcProtocol)
} else if let swiftMetatype = object as? Any.Type {
return String(reflecting: swiftMetatype)
} else {
crash("Bridge identifiers must be metatypes -- got \(proto) of type \(type(of: proto))")
}
}
The fix was to always cast to AnyObject first:
// We NEED to use this as an AnyObject to force Swift to convert metatypes
// to their Objective-C counterparts. If we don't do this, they are treated as
// different objects and we get different results.
let object = proto as AnyObject
//
I was expecting String(reflecting: Proto.self)
to return OBJCProto
(the actual Obj-C name of the protocol), and for ObjectIdentifier(Proto.self) == ObjectIdentifier(objcProtocol!)
to be true regardless of how I pass the metatype into it. I've found this inconsistency to be pretty confusing (especially since you don't know when the bridge is kicking in)