Skip to content

TyphoonAssembly

Jasper Blues edited this page Apr 4, 2015 · 27 revisions
"Any magic, sufficiently analyzed is indistinguishable from technology."

Clarke C. Arthur ;)

Here will take a look at how Typhoon assemblies are instrumented at run-time to allow the following feature:

  • Before activation your assemblies return an instance of TyphoonDefinition
  • After activation an instance built according to the rules in the definition is returned.

#Assembly Instrumentation

Typhoon uses a programming paradigm called Aspect Oriented Programming (AOP) to instrument the methods of your assembly classes with additional behavior.

AOP is used to encapsulate "cross-cutting" concerns. These are the kind of requirements that "cut across" many modules in your system. In our case the requirement is:


Whenever a new definition is created it should be assigned
a unique key based on the method name that defined it. 

Assembly AOP

AOP defines the following kinds of method interceptions:

Interception Description
Before Additional behavior is added before a method invocation.
After Additional behavior is added after a method invocation.
After Throwing Additional behavior is added after an exception occurs.
Around A flexible style that can be used to model any of the above.

TyphoonAssembly instrumentation uses the 'After' style - after a definition is returned it as allocated a unique key based on the method name that defined it.

##Registration into TyphoonComponentFactory

  • After instrumentation each of the definitions in your assemblies - we can think of these as blue-prints for creating a an instance - are registered into a TyphoonComponentFactory
  • The TyphoonComponentFactory then runs its' TyphoonDefinitionPostProcessor system to modify these recipes for creating an instance. Post processors can be plugged in to perform things like configuration.

#How is method interception achieved?

Method interception is achieved using the Objective-C run-time. Objective-C is a late binding language or in other words it uses messaging or dynamic dispatch to achieve method invocation. This provides the ability to forward method invocations to an alternative implementation, optionally calling the original, and is known as swizzling.

Swizzling comes in a few flavors:

Swizzle Effect
Method swizzling Forward invocations of a method for all instances of a given class, optionally calling the original, at some point.
isa pointer swizzling Forward invocations of a method for a specific instance of a given class, optionally calling the original, at some point.

NB: Other languages that use static dispatch can achieve 'late binding' by virtue of having a virtual machine and class loader system, which provides an alternative hook point.

##A hack? Not at all

Swizzling has sometimes been described in informal discussion groups as a hack. Like any powerful technique it can be easily misused, however it in fact the foundation of many of Cocoa's most powerful features, including:

  • Property Observers (KVO) : Property setter is intercepted to notify subscribers when a new value is supplied.
  • Core Data Managed Objects
  • etc

#A Cat, All Dressed Up as a Duck

  • Before activation your assemblies return an instance of TyphoonDefinition
  • After activation an instance built according to the rules in the definition is returned.

To achieve that latter requirement, at runtime an instance of TyphoonComponentFactory actually poses as your assembly class.

  • When an Objective-C method is invoked for a given instance, a look-up table is consulted to resolve that method to a function pointer.
  • When a method that does not exist for that class is invoked, before raising an error, the object is given the opportunity to forward the invocation to an alternative endpoint, again thanks to Objective-C's messaging system.

Invocations of TyphoonComponentFactory methods are redirected to the method

-(id) componentForKey:(NSString*)key

. . using the unique key that was generated when instrumenting the assembly.


#Benefits

We use a meta programming DSL to define dependency injection container with rules for assembling instances.

  • No magic strings - use regular IDE refactoring tools for code-completion, method renaming, etc.
  • Allows collaborating assemblies / modularization
  • Allow more than one component of the same type by supporting explicit wiring as well as auto-wiring. (Objective-C is weak on auto-wiring in any case - type information is only returned for properties).
  • Does not require any IDE plugins or special tool support.