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.
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:
To create an autoreleased Foo object, we’d write
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:
return [[[Foo alloc] init] autorelease];
Looks fine. Everything works. So what’s the problem?
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:
What happens if we send the earlier factory method to this subclass?
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.
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:
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:
+ (NSString *)transformString:(NSString *)string;
To invoke this class method from outside of Foo, you’d say
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
Remember, self in a class method refers to the class object.
From an instance method, use
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.
Did you find this useful? Subscribe today to get regular posts on clean iOS code.