Taking Notes

cause i'll forget


A fairly normal description of my side of most conversations with Rob Rix:

(1:00PM) That’s a terrible idea

(1:05PM) Ok, it’s not that terrible, but why would you want to do that?

(1:10PM) Ok, it’s ridiculous, but I’m intrigued

(1:15PM) Ok, I’ll be back in a few, I’m gonna build this

Today the discussion was the idea that you could implement a class cluster that only declares a protocol as it’s public interface.

Something like:

@protocol ClassClusterPublicInterface <NSObject>
- (id)initWithTypeA:(id)a;
- (id)initWithTypeB:(id)b;
- (void)doSomething;

and then if you could figure out a way to get instances out of that protocol declaration, you’d be in business. Turns out @protocol is implemented as the the object Protocol, defined in <objc/protocol.h> and since it’s an object, you can add to it via categories:

@interface Protocol (Alloc)
- (void)registerClass:(Class)class forInitializer:(SEL)initializer;
- (id)alloc;

and now you can instantiate and use an object like this:

id clusterA = [[@protocol(ClassClusterPublicInterface) alloc] initWithTypeA:nil];
[clusterA doSomething];

2013-01-09 14:09:42.270 Protocol[3433:303] Do A

You can check out the details of the implementation on github but the core of it is storing the class to use for a given protocol, initializer combo in load:

+ (void)load
    if (self == [ClassCluster class])
        [@protocol(ClassClusterPublicInterface) registerClass:[ConcreteSubclassA class] forInitializer:@selector(initWithTypeA:)];
        [@protocol(ClassClusterPublicInterface) registerClass:[ConcreteSubclassB class] forInitializer:@selector(initWithTypeB:)];

and then using –forwardingTargetForSelector: to return an alloced instance of that class when that initializer is called on Protocol.

This doesn’t have much in the way of practical relevance, but it’s good fun and worth giving a look.