- 本文會(huì)闡述下面幾個(gè)問(wèn)題
1、什么是id類(lèi)型
2、id類(lèi)型的賦值問(wèn)題
3、id類(lèi)型對(duì)象在調(diào)用方法的時(shí)候編譯期和運(yùn)行時(shí)的規(guī)則
4、NSObject類(lèi)型與id類(lèi)型的區(qū)別
5、instancetype與id類(lèi)型的區(qū)別
查看源碼(源碼版本objc4-781.2)

可以看到有兩個(gè)頭文件定義了id類(lèi)型,我們進(jìn)一步打開(kāi)objc.h查看源碼,發(fā)現(xiàn)有一個(gè)預(yù)編譯宏#if !OBJC_TYPES_DEFINED,意思是沒(méi)有定義Objective-C再編譯里面的內(nèi)容
#if !OBJC_TYPES_DEFINED
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;
/// Represents an instance of a class.
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
/// A pointer to an instance of a class.
typedef struct objc_object *id;
#endif
再打開(kāi)objc-private.h查看源碼,就是它了,嗯~沒(méi)啥區(qū)別,所以id類(lèi)型是一個(gè)結(jié)構(gòu)體指針
typedef struct objc_object *id;
objc_object結(jié)構(gòu)體簡(jiǎn)略定義如下:
struct objc_object {
private:
isa_t isa;
public:
// ISA() assumes this is NOT a tagged pointer object
Class ISA();
// rawISA() assumes this is NOT a tagged pointer object or a non pointer ISA
Class rawISA();
// getIsa() allows this to be a tagged pointer object
Class getIsa();
...
};
內(nèi)部定義了isa_t類(lèi)型的isa聯(lián)合體,和一系列操作isa的功能函數(shù),有關(guān)isa和Class在后續(xù)的文章會(huì)討論
id類(lèi)型為什么能接受任意類(lèi)型的對(duì)象賦值
實(shí)際上OC里面任意類(lèi)型的對(duì)象都能用非繼承體系的對(duì)象賦值,如下四個(gè)賦值語(yǔ)句都能順利通過(guò)編譯
1、NSString *arr_str = [NSArray new];
2、NSDictionary *arr_dic = [NSArray new];
3、NSArray *arr_arr = [NSArray new];
4、id arr_id = [NSArray new];
只是前三個(gè)在編譯期會(huì)有類(lèi)型檢查,前兩個(gè)編譯器會(huì)報(bào)出類(lèi)型不匹配警告,id類(lèi)型的對(duì)象編譯器會(huì)跳過(guò)類(lèi)型檢查
5、[arr_str count];
6、[arr_dic count];
7、[arr_arr count];
8、[arr_id count];
如上,添加四句代碼后能否通過(guò)編譯?答案是6,7可以通過(guò)編譯,5會(huì)報(bào)No visible @interface for 'NSString' declares the selector 'count',8會(huì)報(bào)Multiple methods named 'count' found with mismatched result, parameter type or attributes
重點(diǎn)看下第8行代碼報(bào)的錯(cuò)誤,竟然查詢到多個(gè)同名方法,編譯器不知道用哪個(gè),為什么呢?
這是因?yàn)閕d類(lèi)型的對(duì)象,跳過(guò)了編譯器的類(lèi)型檢查,導(dǎo)致編譯器在查詢方法符號(hào)的時(shí)候是在當(dāng)前文件的可訪問(wèn)范圍內(nèi)查找,我們知道Foundation框架里面的容器類(lèi)都實(shí)現(xiàn)了count方法,所以自然不知道用哪個(gè)了,有人會(huì)問(wèn),為什么不是查詢到一個(gè)就返回正確的結(jié)果呢,這是因?yàn)樵诰幾g期,編譯器會(huì)做方法唯一性校驗(yàn),那么如何騙過(guò)編譯器的這種校驗(yàn)?zāi)兀?/h6>
我們?cè)偬砑?行代碼
9、[arr_str performSelector:@selector(count)];
10、[arr_dic performSelector:@selector(count)];
11、[arr_arr performSelector:@selector(count)];
12、[arr_id performSelector:@selector(count)];
發(fā)現(xiàn)上面方法都可通過(guò)編譯,原因是performSelector方法同樣跳過(guò)了編譯器的類(lèi)型檢查,所以建議少用這種調(diào)用方式,那么上面的四個(gè)方法運(yùn)行時(shí)會(huì)不會(huì)有問(wèn)題呢,答案是不會(huì)有問(wèn)題,這是因?yàn)閷?duì)象的isa指針指向了該對(duì)象的真實(shí)類(lèi)對(duì)象NSArray,自然能通過(guò)消息查找機(jī)制找到的count方法
NSObject類(lèi)型與id類(lèi)型的區(qū)別
iOS中NSObject是所有類(lèi)的根類(lèi),你會(huì)說(shuō)還有個(gè)NSProxy呢,嗯,這個(gè)也后續(xù)再討論~,id是什么我們上面已經(jīng)討論了,NSObject是一個(gè)OC的類(lèi),自然在編譯期是要進(jìn)行類(lèi)型檢查的
instancetype與id類(lèi)型的區(qū)別
看小標(biāo)題就大概知道了吧,我沒(méi)有在instancetype后面添加類(lèi)型兩個(gè)字,原因是instancetype是編譯器保留的關(guān)鍵字,只用作函數(shù)返回值使用,進(jìn)行類(lèi)型檢查,判斷返回的對(duì)象類(lèi)型是不是當(dāng)前類(lèi)自己