231

Clang adds a keyword instancetype that, as far as I can see, replaces id as a return type in -alloc and init.

Is there a benefit to using instancetype instead of id?

griotspeak
  • 13,022
  • 13
  • 43
  • 54
  • 10
    No.. not for alloc and init, as they already work like this. The point of instancetype is so that you can give custom methods alloc/init like behavoir. – hooleyhoop Mar 06 '12 at 11:40
  • @hooleyhoop they don't work like this. They return id. id is 'an Obj-C object'. That is all. The compiler knows nothing about the return value other than that. – griotspeak Jan 01 '13 at 00:52
  • 5
    they do work like this, the contract and type checking is already happening for init, only for custructors do you need this. http://nshipster.com/instancetype/ – kocodude Jun 21 '13 at 23:18
  • Things have advanced quite a bit, yes. Related result types are relatively new and other changes mean that this will be a much clearer issue soon. – griotspeak Jun 22 '13 at 03:34
  • 1
    I just would like to comment that now on iOS 8 a lot of methods that used to return `id` have been replaced with `instancetype`, even `init` from `NSObject`. If you want to make you code compatible with swift you have to use `instancetype` – Hola Soy Edu Feliz Navidad Jul 22 '14 at 16:20

5 Answers5

343

Yes, there are benefits to using instancetype in all cases where it applies. I'll explain in more detail, but let me start with this bold statement: Use instancetype whenever it's appropriate, which is whenever a class returns an instance of that same class.

In fact, here's what Apple now says on the subject:

In your code, replace occurrences of id as a return value with instancetype where appropriate. This is typically the case for init methods and class factory methods. Even though the compiler automatically converts methods that begin with “alloc,” “init,” or “new” and have a return type of id to return instancetype, it doesn’t convert other methods. Objective-C convention is to write instancetype explicitly for all methods.

With that out of the way, let's move on and explain why it's a good idea.

First, some definitions:

 @interface Foo:NSObject
 - (id)initWithBar:(NSInteger)bar; // initializer
 + (id)fooWithBar:(NSInteger)bar;  // class factory
 @end

For a class factory, you should always use instancetype. The compiler does not automatically convert id to instancetype. That id is a generic object. But if you make it an instancetype the compiler knows what type of object the method returns.

This is not an academic problem. For instance, [[NSFileHandle fileHandleWithStandardOutput] writeData:formattedData] will generate an error on Mac OS X (only) Multiple methods named 'writeData:' found with mismatched result, parameter type or attributes. The reason is that both NSFileHandle and NSURLHandle provide a writeData:. Since [NSFileHandle fileHandleWithStandardOutput] returns an id, the compiler is not certain what class writeData: is being called on.

You need to work around this, using either:

[(NSFileHandle *)[NSFileHandle fileHandleWithStandardOutput] writeData:formattedData];

or:

NSFileHandle *fileHandle = [NSFileHandle fileHandleWithStandardOutput];
[fileHandle writeData:formattedData];

Of course, the better solution is to declare fileHandleWithStandardOutput as returning an instancetype. Then the cast or assignment isn't necessary.

(Note that on iOS, this example won't produce an error as only NSFileHandle provides a writeData: there. Other examples exist, such as length, which returns a CGFloat from UILayoutSupport but a NSUInteger from NSString.)

Note: Since I wrote this, the macOS headers have been modified to return a NSFileHandle instead of an id.

For initializers, it's more complicated. When you type this:

- (id)initWithBar:(NSInteger)bar

…the compiler will pretend you typed this instead:

- (instancetype)initWithBar:(NSInteger)bar

This was necessary for ARC. This is described in Clang Language Extensions Related result types. This is why people will tell you it isn't necessary to use instancetype, though I contend you should. The rest of this answer deals with this.

There's three advantages:

  1. Explicit. Your code is doing what it says, rather than something else.
  2. Pattern. You're building good habits for times it does matter, which do exist.
  3. Consistency. You've established some consistency to your code, which makes it more readable.

Explicit

It's true that there's no technical benefit to returning instancetype from an init. But this is because the compiler automatically converts the id to instancetype. You are relying on this quirk; while you're writing that the init returns an id, the compiler is interpreting it as if it returns an instancetype.

These are equivalent to the compiler:

- (id)initWithBar:(NSInteger)bar;
- (instancetype)initWithBar:(NSInteger)bar;

These are not equivalent to your eyes. At best, you will learn to ignore the difference and skim over it. This is not something you should learn to ignore.

Pattern

While there's no difference with init and other methods, there is a difference as soon as you define a class factory.

These two are not equivalent:

+ (id)fooWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;

You want the second form. If you are used to typing instancetype as the return type of a constructor, you'll get it right every time.

Consistency

Finally, imagine if you put it all together: you want an init function and also a class factory.

If you use id for init, you end up with code like this:

- (id)initWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;

But if you use instancetype, you get this:

- (instancetype)initWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;

It's more consistent and more readable. They return the same thing, and now that's obvious.

Conclusion

Unless you're intentionally writing code for old compilers, you should use instancetype when appropriate.

You should hesitate before writing a message that returns id. Ask yourself: Is this returning an instance of this class? If so, it's an instancetype.

There are certainly cases where you need to return id, but you'll probably use instancetype much more frequently.

Steven Fisher
  • 44,462
  • 20
  • 138
  • 192
  • 4
    It has been a while since I asked this and I have long since taken up a stance like the one you put forth here. I let it go here because I think that, unfortunately, this will be considered an issue of style for init and the information presented in the earlier responses did answer the question pretty clearly. – griotspeak Feb 02 '13 at 04:21
  • 6
    To be clear: I believe the answer by Catfish_Man is correct **only in its first sentence**. The answer by hooleyhoop is correct **except for its first sentence**. It's a hell of a dilemma; I wanted to provide something that, over time, would be seen to be more useful and more correct than either. (With all due respect to those two; after all, this is much more obvious now than it was back when their answers were written.) – Steven Fisher Feb 05 '13 at 19:19
  • I think you mean that everything except Catfish_Man's second sentence is correct. – griotspeak Feb 05 '13 at 19:24
  • Hmm. It's ambiguous, I suppose, which means I'm probably interpreting it incorrectly. When you use `id`, you do indeed get type checking, but only in certain circumstances. – Steven Fisher Feb 05 '13 at 19:51
  • I expanded the introduction to make my answer more clear, and to make my problems with the other answers (though not explicitly called out) more clear. – Steven Fisher Feb 05 '13 at 20:03
  • Good explanation. Do you expect the Foundation classes to switch and adhere to this principle? E.g., methods like `[NSArray arrayWithObjects: ...]` should (and will?) return `instancetype`? – Jean-Philippe Pellet Mar 06 '13 at 12:03
  • 1
    Over time I'd hope so. I can't think of any reason not to, unless Apple feels it's important to maintain source compatibility with (for instance) assigning a new NSArray to a NSString without a cast. – Steven Fisher Mar 06 '13 at 17:19
  • clang 3.3 will interpret `id` as `instancetype` when certain typical conditions are met. convenient, but it makes it unlikely that Apple will change the foundation classes. – griotspeak May 06 '13 at 19:04
  • You mean for class methods? Have you seen the conditions anywhere? I'd love to know what they are. – Steven Fisher May 06 '13 at 22:59
  • Here you go http://clang.llvm.org/docs/LanguageExtensions.html#related-result-types – griotspeak May 14 '13 at 19:00
  • Ah, I thought you meant over and above what I was describing. :) Edit: I added a link to that page, though. Thanks. – Steven Fisher May 14 '13 at 23:42
  • Whether you use `id` or `instancetype` on `init` methods is a style decision. There are reasons for both approaches, depending on your coding style one or the other may be more suited. I think that the reasons for using `id` outweigh those for using `instancetype` on `init` methods. – Tammo Freese Aug 03 '13 at 10:04
  • 4
    `instancetype` vs `id` really isn't a style decision. The recent changes around `instancetype` really make it clear that we should use `instancetype` in places like `-init` where we mean 'an instance of my class' – griotspeak Aug 04 '13 at 02:40
  • As the two ways are technically the same and people have different opinions on which to use, it is a style decision. I think that as `- (id)init...` methods have returned instances of the respective class in Objective-C for more than 20 years now, there is no clarification needed. If you try to return something else you get a compiler error anyway now. That said, I totally respect your choice of another style, and there are both great programmers out there that use the style you prefer, and that use the style I prefer. Peace? – Tammo Freese Aug 04 '13 at 09:42
  • 2
    You said there were reasons for using id, can you elaborate? The only two I can think of are brevity and backwards compatibility; considering both are at the expense of expressing your intent, I think those are really poor arguments. – Steven Fisher Aug 07 '13 at 22:10
  • Hi Steven, the convention that `- (id)init` methods in Objective-C return an instance of the respective class is in place for decades now. Suddenly using a new keyword to make that more expressive is more confusing than helpful for most, because most do not realize that it does not make any difference to the compiler, and adds no safety at all. You may consider it expressive, I consider it as writing a longer term for something that a user of the language has to learn anyway to understand all `init` methods that were written before `instancetype` existed as a keyword. – Tammo Freese Oct 30 '13 at 22:09
  • What you're saying is you've learned that an id from an init is a type of thing than an id elsewhere. This is you having trained yourself to view id differently in different places. I get that, but I think the only question is if that's caused you problems *yet*, rather than if it will at all. – Steven Fisher Oct 30 '13 at 22:43
  • Try to find Objective-C programmers that do not know that `init` methods return instances of the respective type. This is common knowledge that's now even enforced by the compiler! `instancetype` is a recent addition, I think the trend to convert lots of `init` methods to `instancetype` started as earlier versions of the compiler weren't relating the result type, so that using `instancetype` on `init` methods actually made a technical difference. Even in iOS 7, lots of classes are still `- (id)init`, new classes are using it at well. I won't think less of any programmer that uses either style. – Tammo Freese Oct 31 '13 at 17:35
  • 1
    Me either, but I think this is a bad argument. Trying to find Objective-C programmers that do not know the `id` returned by `init` is different than the `id` returned by other methods is really easy, and getting easier every day. The mitigating circumstance is that returning `id` from other types of methods is fairly uncommon. I don't think it's a good recommendation, that's all. :) – Steven Fisher Oct 31 '13 at 20:14
  • @TammoFreese The only places that id has the exact same behaviour is with the standard, agreed upon, method names see [Related Result Types](http://clang.llvm.org/docs/LanguageExtensions.html#related-result-types). Methods convenience methods had no way consistently express that they returned an instance of the current class. One could infer or documentation could specify, but there was no agreed upon way to signify what was occurring. There is also the case where old code that we THOUGHT was returning `instancetype` was, very often, not doing that because a class was accidentally hard coded. – griotspeak Nov 26 '13 at 04:37
  • @TammoFreese I would like to point out that instancetype isn't really 'enforced' by the compiler when you use `id`. The compiler infers and forgives but it does not force you to call `[self class]` which would, in my opinion, be 'enforcement' of `instancetype`. I am not even sure that it protects against hard coding the class with `instancetype`, though I wish it did. – griotspeak Nov 26 '13 at 04:40
  • @griotspeak Yes, it is only enforced for the related result types. Maybe enforced is the wrong term for this, I am not a native speaker, so I might have chosen the wrong one. With "enforcement" I meant here that if you try something like `A *a = [[B alloc] init]` you will get a compiler error, whether the `init`'s return type is declared as `id` or `instancetype` does not matter. I am totally for using `instancetype` for convenience methods, that's why it has been introduced in the first place. – Tammo Freese Nov 26 '13 at 05:45
  • @TammoFreese Ah, that is a type of enforcement. Sorry, I didn't consider that. I guess my point was really that it doesn't go all of the way to enforcing it. – griotspeak Nov 26 '13 at 16:13
  • That's true. But as you noted, if you do `[MSMutableString string]` on an SDK where it returned `id`, you could assign it to `A *a`. `instancetype` is more a question of consistency and clarity of intent than a hard requirement. I maintain that you're better off learning and using it wherever it actually applies, rather than just where the compiler won't promote to it. (And, after all, those rules could change.) – Steven Fisher Nov 26 '13 at 16:15
  • 1
    Sorry for the late comment here, but what am I missing? I get the value of instancetype, but if you are declaring a convenience ctor, why not just make the return value explicit to the class being constructed? Like `- (MyCoolClass *)initWithCoolness:(BOOL)affectCool` Wouldn't this solve the issue? I know it is not conventional, but is there some deeper reason why we don't do this? – Raconteur Apr 11 '14 at 17:11
  • 1
    Because you're not returning an object of that class. You're returning an object of whatever type you sent alloc to. Imagine the chaos if NSObject's init returned an NSObject. :) – Steven Fisher Apr 12 '14 at 18:45
  • 1
    Why use instancetype when you can explicitly specify the class? Like this:+ (ClassName)fooWithBar:(NSInteger)bar; // class factory – mskw Aug 14 '15 at 18:24
200

There definitely is a benefit. When you use 'id', you get essentially no type checking at all. With instancetype, the compiler and IDE know what type of thing is being returned, and can check your code better and autocomplete better.

Only use it where it makes sense of course (i.e. a method that is returning an instance of that class); id is still useful.

Catfish_Man
  • 41,261
  • 11
  • 67
  • 84
  • hmm, so it is exactly as simple as that then? I guess compiler warnings was the bit I didn't think about. – griotspeak Jan 23 '12 at 21:57
  • 8
    `alloc`, `init`, etc. are automatically promoted to `instancetype` by the compiler. That isn't to say there's no benefit; there is, but it isn't this. – Steven Fisher Feb 05 '13 at 19:17
  • 10
    it's useful for convenience constructors mostly – Catfish_Man Feb 05 '13 at 19:38
  • @StevenFisher Any idea why Apple’s own convenience constructors (e.g., `[NSMutableArray array]` still return `id` and not `instancetype`? This allows weird stuff like in [this question](http://stackoverflow.com/questions/15248437/in-objective-c-why-am-i-allowed-to-assign-an-nsarray-to-an-nsmutablearray-witho). – Jean-Philippe Pellet Mar 06 '13 at 13:59
  • I imagine it's legacy; some of these headers are 19 years old. I'm not sure exactly how old instancetype is, but it can't be more than 3 years (at MOST). It's probably worth filing a bug report for it. (I should, too.) – Steven Fisher Mar 06 '13 at 17:17
  • 5
    It was introduced with (and to help support) ARC, with the release of Lion in 2011. One thing complicating widespread adoption in Cocoa is that all the candidate methods have to be audited to see if they do [self alloc] rather than [NameOfClass alloc], because it would be super confusing to do [SomeSubClass convenienceConstructor], with +convenienceConstructor declared to return instancetype, and have it not return an instance of SomeSubClass. – Catfish_Man Mar 06 '13 at 18:00
  • Good explanation for the explanation. And thanks for the correction: I thought it was pretty recent, but I figured overestimating would be better than underestimating. – Steven Fisher Mar 07 '13 at 01:13
  • See also https://developer.apple.com/library/prerelease/mac/releasenotes/Foundation/RN-Foundation/index.html#//apple_ref/doc/uid/TP30000742-CH2-SW49 – Catfish_Man Jun 11 '13 at 20:58
  • `id` is automatically converted to `instancetype` by the compiler. From the [clang docs](http://clang.llvm.org/docs/LanguageExtensions.html#related-result-types) "To determine whether a method has an inferred related result type, the first word in the camel-case selector (e.g., “init” in “initWithObjects”) is considered…" – ryanrhee Sep 30 '13 at 21:25
  • 2
    'id' is only converted to instancetype when the compiler can infer the method family. It most certainly does not do so for convenience constructors, or other id-returning methods. – Catfish_Man Oct 01 '13 at 00:28
  • 4
    Note that in iOS 7, many methods in Foundation have been converted over to instancetype. I've personally seen this catch at least 3 cases of incorrect pre-existing code. – Catfish_Man Oct 01 '13 at 00:29
  • Yes. That change caught an infrequently-seen crash in my code, too, @Catfish_Man. – Steven Fisher Jan 22 '14 at 19:43
  • 1
    Now with Swift, instancetype will import as a more useful type than just 'AnyObject!', which is nice :) – Catfish_Man Jun 26 '14 at 17:31
12

Above answers are more than enough to explain this question. I would just like to add an example for the readers to understand it in terms of coding.

ClassA

@interface ClassA : NSObject

- (id)methodA;
- (instancetype)methodB;

@end

Class B

@interface ClassB : NSObject

- (id)methodX;

@end

TestViewController.m

#import "ClassA.h"
#import "ClassB.h"

- (void)viewDidLoad {

    [[[[ClassA alloc] init] methodA] methodX]; //This will NOT generate a compiler warning or error because the return type for methodA is id. Eventually this will generate exception at runtime

    [[[[ClassA alloc] init] methodB] methodX]; //This will generate a compiler error saying "No visible @interface ClassA declares selector methodX" because the methodB returns instanceType i.e. the type of the receiver
}
Evol Gate
  • 2,247
  • 3
  • 19
  • 37
  • this will only generate a compiler error if you do not use #import "ClassB.h". otherwise, compiler will assume that you know what you're doing. for example, the following compiles, but crashes at runtime: id myNumber = @(1); id iAssumedThatWasAnArray = myNumber[0]; id iAssumedThatWasADictionary = [myNumber objectForKey:@"key"]; – OlDor Jan 27 '16 at 22:43
1

You also can get detail at The Designated Initializer

**

INSTANCETYPE

** This keyword can only be used for return type, that it matches with return type of receiver. init method always declared to return instancetype. Why not make the return type Party for party instance, for example? That would cause a problem if the Party class was ever subclassed. The subclass would inherit all of the methods from Party, including initializer and its return type. If an instance of the subclass was sent this initializer message, that would be return? Not a pointer to a Party instance, but a pointer to an instance of subclass. You might think that is No problem, I will override the initializer in the subclass to change the return type. But in Objective-C, you cannot have two methods with the same selector and different return types (or arguments). By specifying that an initialization method return "an instance of the receiving object," you would never have to worry what happens in this situation. **

ID

** Before the instancetype has been introduced in Objective-C, initializers return id (eye-dee). This type is defined as "a pointer to any object". (id is a lot like void * in C.) As of this writing, XCode class templates still use id as the return type of initializers added in boilerplate code. Unlike instancetype, id can be used as more than just a return type. You can declare variables or method parameters of type id when you are unsure what type of object the variable will end up pointing to. You can use id when using fast enumeration to iterate over an array of multiple or unknow types of objects. Note that because id is undefined as "a pointer to any object," you do not include an * when declaring a variable or object parameter of this type.

Nam N. HUYNH
  • 169
  • 2
  • 8
0

The special type instancetype indicates that the return type from the init method will be the same class as the type of object it is initializing (that is, the receiver of the init message). This is an aid for the compiler so that it can check your program and flag potential type mismatches—it determines the class of the returned object based on context; that is, if you’re sending the init message to a newly alloc’ed Fraction object, the compiler will infer that the value returned from that init method (whose return type has been declared as type instancetype) will be a Fraction object. In the past the return type from an initialization method was declared as type id. This new type makes more sense when you consider subclassing, as the inherited initialization methods cannot explicitly define the type of object they will return.

Initializing Objects, Stephen G. Kochan, Programming in Objective-C, 6th Edition

YoungForest
  • 111
  • 1
  • 7