Search code examples
swiftnsobjectswift-protocolsanyobject

How Does AnyObject Conform to NSObjectProtocol?


This question was inspired by mz2's answer on the question Check for object type fails with "is not a type" error.

Consider an empty Swift class:

class MyClass { }

Attempting to call any NSObjectProtocol methods on an instance of this class will result in a compile-time error:

let obj = MyClass()
obj.isKindOfClass(MyClass.self) // Error: Value of type 'MyClass' has no member 'isKindOfClass'

However, if I cast the instance as AnyObject, my object now conforms to NSObjectProtocol and I can call the instance methods defined by the protocol:

let obj: AnyObject = MyClass()
obj.isKindOfClass(MyClass.self) // true
obj.conformsToProtocol(NSObjectProtocol) // true
obj.isKindOfClass(NSObject.self) // false

My object doesn't inherit from NSObject, but still conforms to NSObjectProtocol. How does AnyObject conform to NSObjectProtocol?


Solution

  • In the Cocoa / Objective-C world, AnyObject is id. Having cast this object to AnyObject, you can send any known Objective-C message to it, such as isKindOfClass or conformsToProtocol. Now, when you say isKindOfClass or conformsToProtocol, you're not in the Swift world any more; you're talking to Cocoa with Objective-C. So think about how Objective-C sees this object. All classes in the Objective-C world descend from some base class; a baseless class like MyClass is impossible. And every base class in the Objective-C world conforms to the NSObject protocol (which Swift calls NSObjectProtocol); that's what it is to be (or descend from) a base class! Therefore, to get it into the Objective-C world, Swift presents MyClass as descending from a special bridging base class SwiftObject which does indeed conform to NSObjectProtocol (as you can see here: https://github.com/apple/swift/blob/master/stdlib/public/runtime/SwiftObject.mm).