AnyObject

When is AnyObject not AnyObject? When it’s a protocol type.

Swift has a type called AnyObject that represents a single reference-counted object, with no available operations.1 This doesn’t sound very useful, but sometimes you’re just using the object for its lifetime (a sort of dynamic RAII), and other times you’re planning to downcast it to a concrete type.

AnyObject can also be used as a generic constraint. If you use T: AnyObject, you’re guaranteed that T will have that single-object-reference representation. This allows you to have weak and unowned references to T, as you might expect.

You can also use AnyObject as a constraint on protocols: protocol MyDelegate: AnyObject. Now the implementers are known to have reference semantics, and with T: MyDelegate you can have weak references to T, as before. You can even have weak references to any MyDelegate, allowing swapping between delegates of different types.

What you might run into, though, is that any MyDelegate is not itself AnyObject.

(What?)

If you try to use any MyDelegate as a T: AnyObject, you’ll find the compiler is unhappy with you. Even though every concrete MyDelegate type is a valid AnyObject type, any MyDelegate itself is not. Why not? Because it carries more information than just a single object reference: it also has a “witness table” pointer, the run-time representation of a protocol conformance. That’s how protocol types (any types) work in Swift: they have the normal value, stored in-line or out-of-line depending on size, plus the additional witness table that’s full of method pointers, basically. When you call a protocol method through an any type, the code at run time will look in the table that was given, and pull out the appropriate implementation of that method, then call it using the value part as self.2

But wait, Objective-C never had this problem! The id <MyDelegate> type doesn’t take up more than a single-object-reference to store! But that’s because ObjC protocols aren’t represented as tables of methods; they’re just promises that the implementing class has methods with particular names. So the “table” for a protocol is the same as the “table” for all of a class’s methods…at the cost of a single namespace for method names (selectors) and a bit of extra overhead on app launch and on method calls. And that’s stored in the type, and every object knows its type, so there’s no secondary pointer attached to the value.

(Was this a good tradeoff? A big question, with a lot more in the balance than just the representation of protocol types. Another time, perhaps.)

So that’s why Swift class-constrained protocol types are just a bit less convenient than ObjC ones. You can still convert values to AnyObject, you can still reference them weakly or unownèdly, you can still trust that they have a fixed cost to copy and move. And you can still use the protocol as a constraint and get all the same effects. But you can’t mix the two, because any MyDelegate does not itself have an AnyObject representation.

  1. This is a lie on Apple OSs, but I encourage everyone to act as if it were true. ↩︎

  2. This is also why methods defined in a protocol extension are statically resolved: they’re not in the table. And separately-compiled modules means it may not be possible to put them in the table. So there’s just one rule, and that’s that only the requirements of the protocol are dynamically dispatched. ↩︎