Proxy Based AOP for Cocoa Touch

UITabBarController is generally used as is, no subclassing required. It creates a UITabBar and manages a list of UIViewControllers, keeping track of the tab in focus, UI creation and everything nice. UITabBarController has a delegate, the UITabBarControllerDelegate protocol. Unfortunately this is not a superset of the UITabBarDelegate protocol, and UITabBarController already implements the UITabBarDelegate protocol itself. So how can I hook into and respond to delegate calls from the managed UITabBar?

Simply replacing the original delegate will break the default functionality. We need to somehow insert our own code to execute before the original delegates call. Or in AOP speak; a before advice.

The Wanted Solution

What we want is a proxy that can forward invocations both to our own delegate, and to the original delegate. The interface for said class looks looks like this:

And then we could use this class to replace an existing delegate with a short piece fo code like this:

See my previous post for the implementation of viewWithKindOfClass:.

Easy enough to use, only thing left is to implement CWDelegateOverrideProxy.

Enter NSProxy

Java only have one root class; java.lang.Object. Objective-C can have many, and Cocoa defines two; NSObject and NSProxy. Both of them conforms to the protocol NSObject, wich can be confusing. What we need to know is that class names, and protocol names have two different name spaces in Objective-C. Imagine it as if you could have a class and an interface with the same name in Java; in Objective-C you can. NSObject class is the root class for all concrete classes, and NSProxy class is the root class for proxies, both implements NSObject protocol allowing them to be interchangeable.

Without much ado, here is the base implementation for the initializer:

Introspection

In Objective-C, just as in Java, an instance can be queried for its class, and conformance to protocols (interface in Java speak). But as a bonus an instance can also be queried for specific methods per instance.

Our CWDelegateOverrideProxy class inherits from NSProxy. And it makes a simple assumption; if the original delegate, or the overriding delegate is capable of something, then so it is. So if queried for type of class, protocol conformance, or implemented methods, then we reply with whatever our managed delegates replies.

The NSObject protocol defines methods, equivalent of the java.lang.reflect package, that allows us to implement this as such:

Invocation Forwarding

The NSObject protocol also defines methods for functionality equivalent of java.lang.reflect.InvocationHandler and hooks to act as a java.lang.reflect.Proxy. The details are different as Objective-C do not enforce calls to only known methods at compile time, which means that at run-time unimplemented methods can be called.

When an unimplemented method is called the run-time will call methodSignatureForSelector:, that can return a NSMethodSignature instance, sort of the equivalent of a java.lang.reflect.Method. The run-time will then use the information from the NSMethodSignature instance to create a correct NSInvocation (other half of a java.lang.reflect.Method) instance, and set the correct arguments. This NSInvocation instance is then used as argument to call forwardInvocation:.

And thus our implementation is completed with:

And that rounds up how to implement a class that handles proxy based AOP, in order to introduce a before advice for delegate methods. For a more complex code base you can look at HessianKit, a framework that uses proxies, invocation forwarding and dynamic class creation in order to use a web service as distributed objects.

2 Comments

  1. Marc Love

    Thanks Fredrik!

    Typo alert: the _overridingDelegate instance variable is misspelled as “_overrideingDelegate” when its being defined as a private instance variable in the interface file.

  2. Fantastic article!

    I may put a little AOP in my new dependency injection for objective-C project:

    https://github.com/jasperblues/spring-objective-c/blob/master/README.md

    One nice thing would be something similar to Spring’s @Configurable to swizzle the intializer, and give dependency injection on an object outside of the context of the container.

Leave a Reply