- 本文會闡述下面幾個(gè)問題
1间螟、什么是id類型
2吴旋、id類型的賦值問題
3、id類型對象在調(diào)用方法的時(shí)候編譯期和運(yùn)行時(shí)的規(guī)則
4厢破、NSObject類型與id類型的區(qū)別
5荣瑟、instancetype與id類型的區(qū)別
查看源碼(源碼版本objc4-781.2)
可以看到有兩個(gè)頭文件定義了id類型,我們進(jìn)一步打開objc.h查看源碼摩泪,發(fā)現(xiàn)有一個(gè)預(yù)編譯宏#if !OBJC_TYPES_DEFINED笆焰,意思是沒有定義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
再打開objc-private.h查看源碼,就是它了见坑,嗯~沒啥區(qū)別仙辟,所以id類型是一個(gè)結(jié)構(gòu)體指針
typedef struct objc_object *id;
objc_object結(jié)構(gòu)體簡略定義如下:
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類型的isa聯(lián)合體,和一系列操作isa的功能函數(shù)鳄梅,有關(guān)isa和Class在后續(xù)的文章會討論
id類型為什么能接受任意類型的對象賦值
實(shí)際上OC里面任意類型的對象都能用非繼承體系的對象賦值叠国,如下四個(gè)賦值語句都能順利通過編譯
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è)在編譯期會有類型檢查孙蒙,前兩個(gè)編譯器會報(bào)出類型不匹配警告项棠,id類型的對象編譯器會跳過類型檢查
5、[arr_str count];
6挎峦、[arr_dic count];
7香追、[arr_arr count];
8、[arr_id count];
如上坦胶,添加四句代碼后能否通過編譯透典?答案是6,7可以通過編譯顿苇,5會報(bào)No visible @interface for 'NSString' declares the selector 'count'
峭咒,8會報(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類型的對象幔翰,跳過了編譯器的類型檢查漩氨,導(dǎo)致編譯器在查詢方法符號的時(shí)候是在當(dāng)前文件的可訪問范圍內(nèi)查找西壮,我們知道Foundation框架里面的容器類都實(shí)現(xiàn)了count方法,所以自然不知道用哪個(gè)了叫惊,有人會問款青,為什么不是查詢到一個(gè)就返回正確的結(jié)果呢,這是因?yàn)樵诰幾g期赋访,編譯器會做方法唯一性校驗(yàn),那么如何騙過編譯器的這種校驗(yàn)?zāi)兀?/h6>
我們再添加4行代碼
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)上面方法都可通過編譯步悠,原因是performSelector方法同樣跳過了編譯器的類型檢查,所以建議少用這種調(diào)用方式瘫镇,那么上面的四個(gè)方法運(yùn)行時(shí)會不會有問題呢鼎兽,答案是不會有問題,這是因?yàn)閷ο蟮膇sa指針指向了該對象的真實(shí)類對象NSArray铣除,自然能通過消息查找機(jī)制找到的count方法
NSObject類型與id類型的區(qū)別
iOS中NSObject是所有類的根類谚咬,你會說還有個(gè)NSProxy呢,嗯尚粘,這個(gè)也后續(xù)再討論~择卦,id是什么我們上面已經(jīng)討論了,NSObject是一個(gè)OC的類郎嫁,自然在編譯期是要進(jìn)行類型檢查的
instancetype與id類型的區(qū)別
看小標(biāo)題就大概知道了吧秉继,我沒有在instancetype后面添加類型兩個(gè)字,原因是instancetype是編譯器保留的關(guān)鍵字泽铛,只用作函數(shù)返回值使用尚辑,進(jìn)行類型檢查,判斷返回的對象類型是不是當(dāng)前類自己