1 Comment

Meta Class Dependency Injection in Objective-C

In a previous post we announced Objection, a dependency injection framework created for Objective-C. Since then, we have been actively using Objection in our iOS projects and we have added a few new features: Protocol Bindings, Eager Singletons, and Meta Class bindings. Meta class bindings are useful when there is an external dependency that is implemented using only class methods.

A common work-around used to deal with this problem is to first create a class wrapper that simply passes through to the class methods, and then use that wrapper object in the injection context. In Objective-C (and other OO languages) the underlying class of an object is an instance. So, when a class method is invoked, it implicitly dereferences to the metaclass instance and invokes the underlying instance method. Since the class is an instance, it isn’t difficult to add support for metaclass bindings in Objection.

Because the metaclass doesn’t have an explicit interface to bind to, it requires a little bit of work to set it up in Objection. Specifically, it requires you to bind the metaclass to a Protocol, so that Objection knows how to associate the metaclass with a particular property on an object.

Example

External Dependency

@interface ExternalUtility
  + (void)doSomething;
@end

@implementation ExternalUtility
  + (void)doSomething {...}
@end


Define Protocol

The protocol definition should contain the list of method signatures you plan to use. It’ll ensure that you do not get “method not found” compile warning when invoking methods through the protocol.

@protocol ExternalUtility
  - (void)doSomething;
@end


Create Binding in Objection Module

This is where you create the binding of the metaclass to the protocol. Taking this step tells Objection to assign the metaclass instance to any property of protocol type ExternalUtility.

@interface MyModule : ObjectionModule
@end

@implementation MyModule
- (void)configure {
  [self bindMetaClass:[ExternalUtility class] toProtocol:@protocol(ExternalUtility)];
}
@end


 

@interface SomeClass
{
  ...
}
// Use 'assign' because a meta class is not subject to the normal retain/release lifecycle.
// It will exist until the application is terminated (Class Initialization -> Application Termination)
// regardless of the number of objects in the runtime that reference it.
@property (nonatomic, assign) id<ExternalUtility> externalUtility
@end