Automagic Factories in Objective-C

by Bob McCune on April 8, 2011

The Factory pattern is a frequently used creational pattern to help abstract the creation of an object from its clients. Although there are a few specializations of this pattern I’ll focus on the most commonly used approach and then look at how we can leverage the Objective-C Runtime to make this solution more robust.

Let’s begin by creating a simple command-line app that creates Animal objects and gets them to speak on command. The various animal objects will all inherit from the abstract Animal class.

@interface Animal : NSObject {
}

- (void)speak;
- (NSString *)key;

@end
@implementation Animal

- (void)speak {
    NSAssert(NO, @"The 'speak' method must be implemented by subclass.");
}

- (NSString *)key {
    // Animal instances will be 'keyed' by their lower case class name
    // Overly simplistic, but will suffice for now
    return [NSStringFromClass([self class]) lowercaseString];
}

@end

With the abstract base class complete let’s create a couple concrete Animal implementations:

@implementation Cat
- (void)speak {
    NSLog(@"Meow, Meow...");
}
@end
@implementation Dog
- (void)speak {
    NSLog(@"Bark, Bark...");
}
@end

Our Cat & Dog objects are ready to go so let’s integrate them into our app and get them to speak.

int main (int argc, const char * argv[]) {

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    Animal *cat = [[Cat alloc] init];
    [cat speak];
    [cat release];

    Animal *dog = [[Dog alloc] init];
    [dog speak];
    [dog release];

    [pool drain];
    return 0;
}

Running this app will produce the following console output:

Meow, Meow...
Bark, Bark...

This works as we’d expect, but the main problem with using the Cat and Dog classes this way is coupling. Even though we’ve defined our variables as abstract Animal types we’re still coupled to the specific subtypes. This means we can’t modify our Animal types or evolve their creation without impacting our client code. This may seem like a trivial matter in this example, but these design decisions can have major impacts on real-world applications. Let’s avoid some of the potential ripple effects by introducing a factory.

Creating Animals

We’ll begin by moving the object creation responsibilities from our client to a new object called AnimalFactory. This factory, as you may have guessed, will be responsible for creating and vending instances of Animal. A simple, but common implementation of this could be defined as follows:

@implementation AnimalFactory

+ (AnimalFactory *)factory {
    return [[[[self class] alloc] init] autorelease];
}

- (Animal *)animalForKey:(NSString *)animalKey {
    Animal *animal = nil;
    if ([animalKey isEqualToString:@"dog"]) {
        animal = [[Dog alloc] init];
    } else if ([animalKey isEqualToString:@"cat"]) {
        animal = [[Cat alloc] init];
    } else {
        NSAssert(NO, @"No Animal found for key:'%@'", animalKey);
    }
    return [animal autorelease];
}

@end

The AnimalFactory will determine which instance to create based on the argument passed to the animalForKey: method. It creates the appropriate instance and returns an autoreleased version of it to the client. To use this new factory in our client we can make the following changes:

AnimalFactory *factory = [AnimalFactory factory];

[[factory animalForKey:@"cat"] speak];
[[factory animalForKey:@"dog"] speak];

If you were to run this example again you would see the same output as before, but the introduction of this factory has helped us resolve two specific limitations in our previous implementation:

  1. The client is no longer coupled to a particular instance of Animal. As our requirements change the factory can return alternate instances (FatCat, HotDog, etc.) without impacting our client code.
  2. The client is no longer in the business of object creation which means how an animal is instantiated is no longer its concern. Additionally, by returning an autoreleased instance we don’t unnecessarily force memory management responsibilities onto the client.

One outstanding problem we have is with the factory itself. Although the client is better decoupled from the specific types and their creation, the factory itself is not. Every time we add a new animal to the app we additionally have to make modifications to the factory.

[Informercial Guy Voice]  There’s got to be a better way!

Enter the Objective-C Runtime

Objective-C is a dynamic language where many decisions are deferred until runtime. This deferment allows for some of the more interesting capabilities of the language and is made possible through the Objective-C Runtime. Although you can successfully use Objective-C without having much awareness of the runtime, understanding how to interact with it can open up some interesting new possibilities.

It would be nice if our AnimalFactory could automatically find and dispense all instances of Animal without us having to continuously modify the factory. Thanks to the runtime, it can.

Automagic Factories

Let’s begin by creating a utility class called RuntimeUtils containing a method to allow us to dynamically find all instances of a particular type. This method will be implemented as follows:

+ (NSArray *)classStringsForClassesOfType:(Class)filterType {

    int numClasses = 0, newNumClasses = objc_getClassList(NULL, 0);
    Class *classList = NULL;

    while (numClasses < newNumClasses) {
        numClasses = newNumClasses;
        classList = realloc(classList, sizeof(Class) * numClasses);
        newNumClasses = objc_getClassList(classList, numClasses);
    }

    NSMutableArray *classesArray = [NSMutableArray array];

    for (int i = 0; i < numClasses; i++) {
        Class superClass = classList[i];
        do {
            // recursively walk the inheritance hierarchy
            superClass = class_getSuperclass(superClass);
            if (superClass == filterType) {
                [classesArray addObject:NSStringFromClass(classList[i])];
                break;
            }
        } while (superClass);
    }

    free(classList);

    return classesArray;
}

This method makes use of two functions defined in <objc/runtime.h>:

  1. objc_getClassList - This function returns a list of all classes registered with the app. We'll loop through this list and then filter its results.
  2. class_getSuperclass - Returns the superclass of a class. We recursively walk the inheritance hierarchy of each class to determine if it's a descendant of Animal. If so, we add its class string to our array.

With our RuntimeUtils class now ready, let's go back and revisit the implementation of our factory object.

Implementing the Magic

We'll change the implementation of our factory class to leverage this new RuntimeUtils class. We'll start by implementing the init method.

- (id)init {
  if ((self = [super init])) {
    NSArray *animalClassStrings = [RuntimeUtils classStringsForClassesOfType:[Animal class]];
    animals = [[NSMutableDictionary alloc] initWithCapacity:[animalClassStrings count]];
      for (id classString in animalClassStrings) {
        Class animClass = NSClassFromString(classString);
        Animal *animal = [[animClass alloc] init];
        [animals setObject:animal forKey:[animal key]];
        [animal release];
      }
    }
  return self;
}

Our init implementation starts by finding all the class strings for the classes extending Animal. We iterate through the results creating an instance of each class and storing its pointer in the animals dictionary. With the init method complete let's modify the animalForKey: method.

- (Animal *)animalForKey:(NSString *)animalKey {
    Animal *animal = [animals objectForKey:animalKey];
    NSAssert(animal, @"No animal found.  Invalid 'animalType' specified?");
    return animal;
}

We now have a greatly simplified animalForKey: method. Gone are the if/else or switch statements you'd typically see in a factory method. Instead, we have a simple, generic execution path regardless of the number of animals we add to the application.

To see the benefits of our revamped AnimalFactory, let's make a couple additions to our animal kingdom.

@implementation Monkey
- (void)speak {
    NSLog(@"Ooo, Ooo, Eee, Eee");
}
@end
@implementation Bird
- (void)speak {
    NSLog(@"Tweet, tweet");
}
@end

We can go back to our client code and make the following changes:

[[factory animalForKey:@"dog"] speak];
[[factory animalForKey:@"cat"] speak];
[[factory animalForKey:@"monkey"] speak];
[[factory animalForKey:@"bird"] speak];

If you run the application again you'll see the new Monkey and Bird instances were automatically registered with the factory without any additional changes! Winning!

Conclusion

Although this was clearly a contrived example, it hopefully illustrates how leveraging the Objective-C Runtime can result in significantly more flexible factory objects. Some simple modifications could be made to this approach to provide for lazy instantiation or handle more complex object creation. In a future post I'll provide a more concrete example of how this strategy can be put to good use in a real-world application.

Resources

Sample Code

Objective-C Runtime Reference

Understanding the Objective-C Runtime

[ 7 comments… read them below or add one ]

Tim Gogolin April 8, 2011 at 5:51 pm

Shouldn’t that be NSLocalizedString(@”Meow, Meow…”, …)?
I was quite suprised how many different sounding spellings different languages have for common animal noises.

Seriously though, this was a nice article; I’d not used the runtime class enumeration to populate class factories before.

Reply

Bob McCune April 8, 2011 at 6:15 pm

LOL. The funny thing is as I was writing this I remembered how my high school Spanish books had the dogs saying “Gah! Gah!”

Thanks!

Reply

John Shields April 9, 2011 at 4:44 pm

Nice article Bob! It’s not always obvious how dynamic Objective-C is. It’s a lot more powerful than I’d first thought.

Reply

Paul Mans April 25, 2011 at 6:54 pm

Very neat technique! Curious though why you decided to store an instance of each animal object in the factory instead of just creating a new one each time the animalForKey method was called?

Reply

Bob McCune April 25, 2011 at 8:00 pm

No particular reason, just illustration. There are a number of valid variants that could be used that leverage this same basic technique. In fact, my actual production usage of this creates a new instance per invocation of the factory method.

Reply

bob July 29, 2011 at 1:21 am

I want to note that class objects are objects just like any other objects. So you can return an array of class objects themselves, instead of converting them to a string and then converting them back.

“We recursively walk the inheritance hierarchy of each class to determine if it’s a descendant of Animal.”
If you assume that the classes all descend from NSObject, then you can use the class method +isSubclassOfClass: to determine if it’s a descendant of Animal (since Animal is technically a subclass of itself, you would manually test for equality to Animal to exclude it if that’s what you want)

Reply

Kevin January 3, 2012 at 5:35 pm

This is a real help Bob, thank you for writing this up.
Would there be any way to do this while the program is running e.g. automagic instantiation of runtime defined classes?

Reply

Leave a Comment