Can someone please explain to me (in simple terms) why an instancetype
is used in Objective-C?
- (instancetype) init {
self = [super init];
if (self) {
// Custom initialization
}
return self;
}
instancetype is a contextual keyword that can be used as a result type to signal that a method returns a related result type. For example: @interface Person + (instancetype)personWithName:(NSString *)name; @end. instancetype , unlike id , can only be used as the result type in a method declaration.
“id” is a data type of object identifiers in Objective-C, which can be use for an object of any type no matter what class does it have. “id” is the final supertype of all objects.
Back in the old days, initialisers just returned an object of type id
(any object).
With normal initialisers (those that begin with "init", "alloc" or "new"), this wasn't usually a problem. The compiler would automatically infer the type that it returned and therefore restrict any method calls on the object to the instance methods of that class.
However, this was a problem with static convenience initialisers or "factory methods" that didn't necessarily follow the same naming convention - therefore it was unable to apply the same type safety.
This means that with a class like this:
@interface Foo : NSObject
+(id) aConvenienceInit;
@end
The compiler would accept code like this:
NSArray* subviews = [Foo aConvenienceInit].subviews;
Why? Because the returned object could be any object, so if you try and access a UIView
property - there's no type safety to stop you.
However, now with instancetype
, the result you get back is of type of your given instance. Now with this code:
@interface Foo : NSObject
+(instancetype) aConvenienceInit;
@end
...
NSArray* subviews = [Foo aConvenienceInit].subviews;
You'll get a compiler warning saying that the property subviews
is not a member of Foo*
:
Although it's worth noting that the compiler will automatically convert the return type from id
to instancetype
if your method begins with "alloc", "init" or "new" - but nonetheless using instancetype
wherever you can is a good habit to get into.
See the Apple docs on instancetype
for more info.
Imagine two classes:
@interface A : NSObject
- (instancetype)init;
@end
@interface B : A
@end
The init
method from A
is inherited to B
. However, in both classes the method has a different return type. In A
the return type is A
and in B
the return type is B
.
There is no other way to declare the return type for initializers correctly. Note that most programming languages with classes don't even have return types for constructors, therefore they completely avoid the issue.
This is the reason why Obj-C needs instancetype
but of course it can be used outside initializers, too.
It is important to use instancetype
instead of id
in Objective-C if you are also using this code in Swift. Consider the following class declaration:
@interface MyObject : NSObject
+ (id)createMyObject;
- (void)f;
@end
If you want to create a MyObject
instance in Swift 5.3 with createMyObject
and then call f
for this object, you will have to do the following:
let a = MyObject.createMyObject()
(a as? MyObject)?.f()
Now replace id
with instancetype
in MyObject
to have the following Swift code:
let a = MyObject.create()
a?.f()
As you can see now, you can use MyObject.create()
instead of MyObject.createMyObject()
. And you don't need to use (a as? MyObject)
since a
is defined as MyObject?
and not as Any
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With