Delegation sucks

written by Martin Häcker on

Well, it doesn't really. But if way overused it becomes a pain in the a**.

This is not so much a problem in on itself, but the code sprouts dependencies that nobody is able to follow from just reading a class but which become crucial to the operation of the application.

Well, out of frustration I started thinking about the saying that Design-Patterns are just for languages that are not powerful enough to implement something in the language.

So lets see, what can we do to make this simpler:

if ([someObject respondsToSelector:@selector(someDelegateMethod:)]) {
    [someObject someDelegateMethod:self];
}

The key here is to only call the messagte -someDelegateMethod: if it is actually supported on the target. A perfect application [wiki:2008/10/26/20.59 of my invocation] [wiki:2008/10/25/01.21 catching code].

[[NMDelegateCaller callerForDelegate:object] someDelegateMethod:self];

In real life of course you would hide the delegator by just wrapping your delegate in the -setDelegate: method.

Interestingly implementing this proved rather more tricky than I anticipated. This is because of the way the objc-runtime creates NSInvocations out of c-method calls. The trick is, that really at runtime nothing is known about how arguments have to be scraped out of the stack because in C you have no clue what arguments actually are on the stack.

So objc takes a detour and stores this information alongside each method in the object that the method belongs to. This however has the unfortunate side effect that it should make just the delegator I wanted to build impossible to build. That is, because the runtime cannot build an NSInvocation out of a method call that the destination does not support - simply because it doesn't know what arguments are on the stack and what to return.

This is where my little evil hack comes in: I defined a method on NSObject that represents an identity for this situation - that is, it returns nothing and takes no argument - therefore the runtime won't crash anything by using it's description to build an NSInvocation. This is my marker.

And with this the problem becomes solvable - in -methodSignatureForSelector: I ask the target for a signature if it has one, and return my marker signature and let the runtime build an NSInvocation out of whatever method call was made (what exactly it was doesn't interest me, only that it's not implemented on the delegate). Then in -forwardInvocation: I can test if the signature is that marker and just discard the method if it is.

Voilá.

So it is proven - objc is powerful enough to implement this design pattern in itself - no need for all those if statements - and I learned a lot about the internals of NSInvocations.

Still I'm not sure if I really want to use that code. Yes it's shorter, but it's also a lot more complicated and it makes something very convenient which in essence I don't really want to be so convenient, because if overused it becomes a maintenance nightmare.

Also it lacks the simple elegance of just stating exactly what you want to achieve and returning good default values (my solution can just return NULL/NO/0 if the method is not implemented).

Damn. Alles fĂĽr die Katz.

Still very interseting though. Maybe I'l find a different application that suits me more.

  • [source:open-source/NMInvocationBuilder/trunk Here's the code]