要區分instancetype和id,首先要弄清楚什么是關聯返回類型(Related Result Type)。
關聯返回類型即一個方法的返回類型就是調用這個方法的調用者的類型。具有下列條件的方法具有關聯返回類型:
1 對於靜態方法,方法名以alloc,new開頭;
2 對於實例方法,方法名以autorelease,init,retain,self開頭
關聯返回類型的作用就是讓編譯器在編譯期就可以進行類型檢測(Type Check),比如下面的例子:
@interface X : NSObject @property (assign, nonatomic) NSInteger index; + (id)create; @end @implementation X + (id)create { X *x = [[X alloc] init]; return x; } @end @interface Y : NSObject /** *工程中如果不存在一個方法叫haha,即使create的返回值是id *編譯器也會報錯:No known instance method for selector 'haha' */ - (void)haha; @end
然后進行如下調用:
[[X create] haha];
由於類X的create方法命名不符合關聯返回類型的要求,因此編譯器無法推斷出該方法返回的就是一個X類型,而是只把返回值看成是id類型。又由於方法haha在工程中存在,因此可以成功編譯。(但是運行時仍然會報錯:unrecognized selector sent to instance)。
為了讓不具備關聯返回類型命名的方法也可以讓編譯器進行正確的推斷,從而在編譯期進行類型檢查,就需要將返回的id類型換成instancetype。如果將上面例子中,把create方法的返回類型換成instancetype,編譯器在編譯的時候就會知道create方法返回的是一個X類型,而X類型沒有聲明haha方法,因此在編譯器就會報錯:
No visible @interface for 'X' declares the selector 'haha'
但是,與id不一樣,id除了作為方法的返回類型,同時可以作為方法的參數以及聲明變量,而instancetype只能作為方法的返回類型。
關於關聯返回類型還有一點,如果子類override了父類的一個關聯返回類型方法,那么子類的返回類型必須要么是子類本身,要么是父類(即可以兼容子類的類型)。
參考資料:
http://blog.csdn.net/kuizhang1/article/details/18048829
http://clang.llvm.org/docs/LanguageExtensions.html#objective-c-features
http://nshipster.com/instancetype/