How to Botch Your Objective-C Factory Method

December 18, 2011 — 9 Comments

We all use factory methods, a.k.a. convenience methods. We often write them ourselves. But there’s a wrong way, and a right way, to write a factory method for your Objective-C class. Do it wrong, and you cut off some features of Objective-C. Do it right, and you open yourself up to new object-oriented possibilities.

Big [self alloc] factory

Volkswagen Factory Wolfsburg/Germany” by Werner Kunz, used under CC BY-NC-SA 2.0 / Altered by Jon Reid

Let’s back up and review what we mean by a factory method. This is a convenience method that you invoke on a class to say, “Make me one of your kind.” (Some folks call this a “convenience constructor” which is odd to me, because the underlying mechanics are quite different from Java or C++.)

Factory method: The wrong way

Let’s say we have a simple class:

@interface Foo : NSObject
- (id)init;
@end

To create an autoreleased Foo object, we’d write

    Foo *aFoo = [[[Foo alloc] init] autorelease];

But if you do this a lot, it becomes tedious and can clutter your code. So let’s create a factory method.

At this point, it’s tempting to copy and paste into the body of the new method:

+ (Foo *)foo
{
    return [[[Foo alloc] init] autorelease];
}

Looks fine. Everything works. So what’s the problem?

Subclass trouble

In Java and C++, a class is a type. When you refer to a static method, you’re explicitly telling the compiler which method or function to use.

Not so in Objective-C.

Objective-C has its object-oriented roots in Smalltalk, where everything is an object. Even a class is an object. When you say [Foo alloc] you’re actually sending a runtime message to the Foo class, which decides what to do with that message.

Now let’s introduce a subclass:

@interface SpicyFoo : Foo
@end

What happens if we send the earlier factory method to this subclass?

    SpicyFoo *aFoo = [SpicyFoo foo];

Ohh, we got trouble, right here in River City! Because it’s implemented the wrong way, the foo convenience method returns a new instance of Foo, not SpicyFoo — even though we were addressing the SpicyFoo class.

Factory method: The right way

Now let’s look at the right way to write a factory method. Then I’ll explain how it works, and why it’s important.

+ (id)foo
{
    return [[[self alloc] init] autorelease];
}

You may say, “Wait, isn’t this a class method? How can you have self in there?” Remember, classes are objects, too. Because this is a class method (as designated by the +), self refers to the class object:

  • If you send this message to a Foo, self is Foo.
  • If you send this message to a SpicyFoo, self is SpicyFoo.

So this solves the problem. No matter what the actual class is, the alloc will be sent to that class. With this new implementation, you can go ahead and send the message foo to SpicyFoo, knowing that the factory method will do the right thing and you’ll get a SpicyFoo instance back.

This also explains why we changed the return type to id: We don’t know what type of object it will return!

Here’s an important application of this pattern: when dealing with mutable and immutable variations, you can send a message to the superclass’s factory method. For example, NSDictionary declares a class method dictionary that returns a new dictionary instance. But you can do this:

    NSMutableDictionary *d = [NSMutableDictionary dictionary];

Even though dictionary is declared against NSDictionary, this returns a new instance of NSMutableDictionary, not NSDictionary.

Expanding beyond factory methods

So there’s a wrong way, and a right way, to write a factory method in Objective-C. Let’s continue to explore the right way, but expand it beyond factory methods.

Back to the Foo class, let’s add another class method — say, something that transforms an NSString:

@interface Foo : NSObject
+ (id)foo;
+ (NSString *)transformString:(NSString *)string;
- (id)init;
@end

To invoke this class method from outside of Foo, you’d say

    NSString *transformed = [Foo transformString:original];

That’s all well and good. But what if you want to invoke it from within Foo itself? Once again, avoid the temptation to copy and paste.

From a class method, use

    NSString *transformed = [self transformString:original];

Remember, self in a class method refers to the class object.

From an instance method, use

    NSString *transformed = [[self class] transformString:original];

We avoid messaging the Foo class from Foo methods, whether they are class methods or instance methods. Why? Because we may be dealing with a subclass. Remember SpicyFoo, the subclass of Foo? By using this approach, SpicyFoo can provide its own implementation of +transformString:. Then code that messages self from a class method, or [self class] from an instance method, gives SpicyFoo the first crack at responding to the message.

Idiom: Don’t refer to your own class by name

Let’s boil it all down to a simple rule of thumb. In Objective-C, don’t refer to your own class by name, because you don’t know what the concrete class will be. You’ll inadvertently chop off the ability to extend and override the class through subclassing. Instead,

  • From a class method: message self
  • From an instance method: message [self class]

This is an Objective-C idiom. Applying this to factory methods, we come back to where we started:

  • To allocate an instance in a class’s factory method: [self alloc]

Go forth and self alloc!

Question: What other object-oriented patterns do we commonly overlook in Objective-C? Leave a comment below.

Jon Reid

Posts Twitter Facebook Google+

I'm passionate about not just improving our code, but improving the way we code.

9 responses to How to Botch Your Objective-C Factory Method

  1. Hi Jon, it was great hangin’ with you in Atlanta.

    This post is most useful to me. Thanks.

  2. Well, even with ARC, it makes self-referencing statics more useful.

    I tend to use a fair number of class methods in my app delegate (sort of a “junk drawer” approach for tools).

    Doing this:

    NSString *transformed = [[self class] transformString:original];

    Is a LOT better than:

    NSString *transformed = [<my class name> transformString:original];

    I’m still larnin’ the ins and outs of Obj-C (Coming from C++), so this was a bit of a revelation.

  3. Returning id is too similar to void* for my tastes. It may be bad enough to negate the use of factory methods. What you seem to be defining is template programming. Well have to talk more but overriding implementations is also open to much debate. I.E. should inheritance only be used for pure abstract methods?

    • Todd,
      Template meta-programming is static duck-typing. What we have in Objective-C is true dynamic duck-typing. I think one importance of returning id is that if a method claims to return a specific type, it’s too easy to assume that what is returned is in fact that type (and not a subclass).

  4. Hi John,

    I’ve just read an article about instancetype at http://nshipster.com/instancetype/ which could extend what you said about factory methods.

    Like you site :)

  5. Janos Tolgyesi June 17, 2013 at 2:57 am

    Hi Jon, thank you for this article. Obviously I face a similar problem and ended up on your site googling the keywords. However I would like to inherit from some framework class so I do not have control over the superclass’ factory method. Say, I would like to inherit from UIImage ensuring the imageNamed: can be used also in my subclass. Do you have any suggestion how to workaround the problem outlined in your article in this case?

    Thanks,
    Janos

Leave a Reply

*

Text formatting is available via select HTML.

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> 

Have you Subscribed yet? Don't miss a post!