instancetype
is a relatively new addition to Objective-C. It was introduced to improve type safety. Specifically, instancetype
improves type safety for methods that return an instance of the type there were called on. Clang refers to these methods as having a “related result type”. init
methods are the most common methods to have a related result type. Other methods with related return type are copy
and class factory methods. Prior to the introduction of instancetype
the compiler was able to infer the return type of init
and copy
thanks to a convention. Because the convention already addresses init
and copy
methods it’s class factory methods that are most improved by the introduction of instancetype
.
Here’s 2 examples that illustrate the improved type safety provided by instancetype
. The first example uses NSURLRequest
which does not use instancetype
for factory methods and the second example is NSString
which does:
Declaring the return type of a method as instancetype
instead of id
allows the compiler to infer the return type for a class and its’ subclasses regardless of the method name.
So that’s the basics of instancetype
. Now let’s look at when to use it, a gotcha when using it, and when not to use an explicit type or id
.
instancetype
Using instancetype
forms implicit documentation that says “subclassing is allowed here”. If a class can be subclassed then we must write the methods to behave correctly when called on a subclass.
@implementation MyClass
+(instancetype)buggyFactoryMethod
{
return [[MyClass alloc] init]; //Wrong!!!
}
+(instancetype)correctFactoryMethod
{
return [[self alloc] init];
}
@end
@implementation MySubClass // this is a subclass of MyClass
//We're not implementing any methods.
@end
When calling buggyFactoryMethod
on MySubClass
an instance of MyClass
will be returned which is not what the API is promising. correctFactoryMethod
does the right thing. It takes advantage of the fact that classes are objects and uses self in the class method to create an instance of the correct type. Subclasses will only need to override correctFactoryMethod
if the subclass changes which method is the designated initialiser.
There’s not much need to write factory methods these days. Back in the pre-ARC world factory methods were much more useful as they reduced the possibility of accidental memory leaks. ARC has removed this potential pitfall and has thus removed much of the reason for adding factory methods to a class; it’s now just as easy to call init
.
init
method should also use instancetype
instead of id
. instancetype
is more explicit and therefore better than id
.
Just as using instancetype
is implicitly says “subclassing is cool”, we can use an explicit type to suggest that “subclassing is probably not wise”. A common usage for explicit types is singletons.
The singleton pattern restricts the instantiation of a class to one instance. In Objective-C the common apporach is to make the instance available with a class method. But what should the return type be:
+(instancetype)sharedInstance; //Wrong!
+(MySingleton *)sharedInstance; //Correct
Using instancetype
as the return type is in conflict with the design of a singleton. The instancetype
return type is saying “if you subclass this method then return an object of the type of the subclass.” If an sharedInstance
is called on the base class then an instance of the base class will be created and return. If sharedInstance
is then called on a subclass then it will either return the object created by the base class, which will be of the wrong type, or it will create another instance and violate the singleton pattern. Therefore singletons should not use instancetype
, they should use an explicit type. (There are actually a few classes in Cocoa that make this mistake.)
Another pattern similar to the singleton pattern is the multiton pattern. The multiton patterns is when an key-value store is used to keep track of instances. NSValueTransformer
uses the multiton pattern to store NSValueTransformer
subclasses by name. This pattern presents a problem: what type should we use when retrieving instances? If we use NSValueTransformer
then we will loose some type information and will have to cast the returned object. To avoid the cast we could use id as the return type. The downside to using id is that we lose all type information. We could use instancetype
but that would result in a type mis-matches when retrieving an instance from calling the method on subclasses. A type mis-match is a lie and is therefore a bad thing that we should avoid. In this situation the best option is to use an explicit type. This means we get some type safety and we are confident that the type is also correct. We could also add a convenience method to subclasses to wrap the call and hide the ugly cast:
+(BECURLTransformer *)urlTransformer
{
id urlTransformer = [NSValueTransformer valueTransformerForName:NSStringFromClass(BECURLTransformer)];
NSAssert([urlTransformer isKindOfClass:[BECURLTransformer class], @"Transformer for key BECURLTransformer is of the wrong type.");
return urlTransformer;
}
Incidentally, it’s worth noting that the compiler gives precedence to the init
naming convention over the return type. For example, UIColor
declares the return type of its init
methods as UIColor
(which I think is a bug in its self). Despite the explicit type the compiler still infers the return type of UIColor
subclasses to be of the subclass type. I think this is incorrect. Using an explicit return type from an init
method would be provide implicit hint that a class should not be subclassed.
The final option is to use id
as the return type. I’m struggling to think of any occasion when id
is the correct choice. instancetype
was introduced to remove the use cases where id
was being used. Using instancetype
or an explicit type are preferable as they let the compiler spot bugs for you.
For more details see: