從C的面向過(guò)程到接觸OC的對(duì)象,、消息的過(guò)渡初期總會(huì)有知其然不知其所以然的糾結(jié),相關(guān)的學(xué)習(xí)資源一般都是介紹有什么,、使用步驟一二三四的套路,這樣就很難知道知道本質(zhì)是什么,能干什么不能干什么,為什么要選擇用它,。而實(shí)際開發(fā)過(guò)程,都是先有什么要解決,,再努力找到實(shí)現(xiàn)方法,。人腦的容易接受的信息,也多是主干到分枝的思維導(dǎo)圖,,綱舉目張,。所以,試著以自己的粗淺理解來(lái)寫一點(diǎn)關(guān)于OC運(yùn)行時(shí)的東西,。 代碼的思想,,大概是把重復(fù)且不變的東西封裝成可以重復(fù)利用的共性,,把變化的東西細(xì)化為具體獨(dú)立松耦合的變量。這些可以是數(shù)據(jù)類型,,也可以是實(shí)現(xiàn)的方法代碼片段,。類也是封裝的產(chǎn)物和可封裝的對(duì)象。被封裝的東西,,需要找到里面內(nèi)容來(lái)具體地實(shí)現(xiàn),,就需要給里面內(nèi)容加個(gè)關(guān)聯(lián)的映射標(biāo)識(shí),比如索引(數(shù)組),、字符串(字典),、指針,、SEL(方法的代號(hào)),、isa(對(duì)象)等等。大概來(lái)說(shuō)就是用類和對(duì)象來(lái)封裝父類指針和方法列表,,用映射來(lái)找到實(shí)現(xiàn)方法的代碼片段,。
主要思路: 實(shí)例對(duì)象instance->類class->方法method(->SEL->IMP)->實(shí)現(xiàn)函數(shù)
實(shí)例對(duì)象只存放isa指針和實(shí)例變量,由isa指針找到所屬類,,類維護(hù)一個(gè)運(yùn)行時(shí)可接收的方法列表 ,;方法列表中的每個(gè)入口是一個(gè)方法(Method) ,其中key是一個(gè)特定名稱,,即選擇器(SEL) ,,其對(duì)應(yīng)一個(gè)指向底層C實(shí)現(xiàn)函數(shù)的指針,即實(shí)現(xiàn)(IMP) ,,,。運(yùn)行時(shí)機(jī)制最關(guān)鍵核心是objc_msgSend函數(shù),通過(guò)給target(類)發(fā)送selecter(SEL)來(lái)傳遞消息,,找到匹配的IMP,,指向?qū)崿F(xiàn)的C函數(shù)。 由于OC的運(yùn)行時(shí)動(dòng)態(tài)特性,,在編譯之后可以在運(yùn)行時(shí)通過(guò)C操作函數(shù),,動(dòng)態(tài)地創(chuàng)建修改類信息,動(dòng)態(tài)綁定方法和重寫實(shí)現(xiàn),,靈活地實(shí)現(xiàn)一些自定義功能,。 紙上寫了個(gè)大綱,沒(méi)有畫思維導(dǎo)圖,,簡(jiǎn)單列個(gè)目錄: 一,、運(yùn)行時(shí)Runtime介紹
二、類的本質(zhì): 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | - 類相關(guān): + 數(shù)據(jù)類型:class,,object,; - isa 元類 - superClass 根類 + 操作函數(shù): - class_: + get: 類名,,父類; 實(shí)例變量,成員變量,;屬性,;實(shí)例方法,類方法,,方法實(shí)現(xiàn),; + copy: 成員變量列表;屬性列表,;方法列表,;協(xié)議列表; + add: 成員變量,;屬性,;方法,;協(xié)議; + replace:屬性;方法,; + respond:響應(yīng)方法判斷(內(nèi)?。?/div> + isMetaclass:元類判斷(內(nèi)?。?/div> + conform:遵循協(xié)議判斷(內(nèi)?。?/div> - objc_: + get: 實(shí)例變量;成員變量,;類名,;類;元類,;關(guān)聯(lián)對(duì)象,; + copy: 對(duì)象;類,;類列表,;協(xié)議列表; + set: 實(shí)例變量,;成員變量,;類;類列表,;協(xié)議,;關(guān)聯(lián)對(duì)象; + dispose: 對(duì)象,; - 動(dòng)態(tài)創(chuàng)建/銷毀類,、對(duì)象 - 成員變量、屬性相關(guān): + 數(shù)據(jù)類型:Ivar,;objc_property_t,;objc_property_attribute_t,; + 操作函數(shù): - ivar_: - property_: - 方法消息相關(guān): + 數(shù)據(jù)類型:SEL;IMP; Method,;方法緩存 + 操作函數(shù): - method_: + invoke: 方法實(shí)現(xiàn)的返回值,; + get: 方法名;方法實(shí)現(xiàn),;參數(shù)與返回值相關(guān),; + set:方法實(shí)現(xiàn); + exchange:交換方法實(shí)現(xiàn) + 方法調(diào)用:msgSend函數(shù)(找到方法實(shí)現(xiàn)) + 消息轉(zhuǎn)發(fā): - Method Resolution - Fast Forwarding - Normal Forwarding - 協(xié)議相關(guān): + 數(shù)據(jù)類型:Protocol,; + 操作函數(shù): - protocol_: + get: 協(xié)議,;屬性; + copy:協(xié)議列表,;屬性列表,; + add:屬性;方法,;協(xié)議,; + isEqual:判斷兩協(xié)議等同,; + comform:判斷是否遵循協(xié)議,; - 其他:類名;版本號(hào),;類信息,;(忽略) | 三、 動(dòng)態(tài)實(shí)現(xiàn): - Method Swizzling;
- ISA Swizzling,;
四,、 其他概念:category;super,;等等,。想起來(lái)再加…
————進(jìn)入正題———– 一、運(yùn)行時(shí)Runtime介紹作用:在程序運(yùn)行的時(shí)候執(zhí)行編譯后的代碼,,可以: | > 動(dòng)態(tài)(創(chuàng)建),、(修改)、(內(nèi)省) `類`和`方法` > 消息傳遞 | 運(yùn)行時(shí)Runtime的一切都圍繞這兩個(gè)中心:類的動(dòng)態(tài)配置 和 消息傳遞,。通過(guò)操作函數(shù)來(lái)配置類信息,,通過(guò)msgSend函數(shù)傳遞消息。 本質(zhì):libobjc.dylib,,C和匯編(消息傳遞機(jī)制由匯編寫成)寫成,。 二、類的本質(zhì):1,、類相關(guān):
數(shù)據(jù)結(jié)構(gòu)(本源):Class類型的結(jié)構(gòu)體,。在objc/runtime.h中查看其成員: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | struct objc_class { Class isa OBJC_ISA_AVAILABILITY; #if !__OBJC2__ Class super_class OBJC2_UNAVAILABLE; // 父類 const char *name OBJC2_UNAVAILABLE; // 類名 long version OBJC2_UNAVAILABLE; // 類的版本信息,,默認(rèn)為0 long info OBJC2_UNAVAILABLE; // 類信息,供運(yùn)行期使用的一些位標(biāo)識(shí) long instance_size OBJC2_UNAVAILABLE; // 類的實(shí)例變量大小 struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; // 類的成員變量鏈表 struct objc_method_list **methodLists OBJC2_UNAVAILABLE; // 方法定義的鏈表 struct objc_cache *cache OBJC2_UNAVAILABLE; // 方法緩存 struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 協(xié)議鏈表 #endif } OBJC2_UNAVAILABLE; | a,、數(shù)據(jù)類型:
isa和super_class :不同的類中可以有相同的方法(同一個(gè)類的方法不能同名,,哪怕參數(shù)類型不同,后面解釋…),,所以要先確定是那個(gè)類,。isa和super_class是找到實(shí)現(xiàn)函數(shù)的關(guān)鍵映射,決定找到存放在哪個(gè)類的方法實(shí)現(xiàn),。(isa用于自省確定所屬類,,super_class確定繼承關(guān)系)。 實(shí)例對(duì)象的isa指針指向類,,類的isa指針指向其元類(metaClass),。對(duì)象就是一個(gè)含isa指針的結(jié)構(gòu)體。類存儲(chǔ)實(shí)例對(duì)象的方法列表,,元類存儲(chǔ)類的方法列表,,元類也是類對(duì)象。 這是id類型的結(jié)構(gòu)(類似于C里面的void *): | struct objc_object { Class isa OBJC_ISA_AVAILABILITY; }; typedef struct objc_object *id; | 當(dāng)創(chuàng)建實(shí)例對(duì)象時(shí),,分配的內(nèi)存包含一個(gè)objc_object數(shù)據(jù)結(jié)構(gòu),,然后是類到父類直到根類NSObject的實(shí)例變量的數(shù)據(jù)。NSObject類的alloc和allocWithZone:方法使用函數(shù)class_createInstance來(lái)創(chuàng)建objc_object數(shù)據(jù)結(jié)構(gòu),。 向一個(gè)Objective-C對(duì)象發(fā)送消息時(shí),,運(yùn)行時(shí)庫(kù)會(huì)根據(jù)實(shí)例對(duì)象的isa 指針找到這個(gè)實(shí)例對(duì)象所屬的類。Runtime庫(kù)會(huì)在類的方法列表由super_class 指針找到父類的方法列表直至根類NSObject中去尋找與消息對(duì)應(yīng)的selector指向的方法,。找到后即運(yùn)行這個(gè)方法,。
metaClass.png | 上圖是關(guān)于isa和super_class指針的圖解: 1、isa:實(shí)例對(duì)象->類->元類->(不經(jīng)過(guò)父元類)直接到根元類(NSObject的元類),,根元類的isa指向自己,; 2、 superclass:類->父類->...->根類NSObject,,元類->父元類->...->根元類->根類,NSObject的superclass指向nil,。 |
b、操作函數(shù):類對(duì)象以class_為前綴,,實(shí)例對(duì)象以object_為前綴 - class_:
get: 類名,,父類,元類,;實(shí)例變量,,成員變量;屬性,;實(shí)例方法,,類方法,,方法實(shí)現(xiàn);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | // 獲取類的類名 const char * class_getName ( Class cls ); // 獲取類的父類 Class class_getSuperclass ( Class cls ); // 獲取實(shí)例大小 size_t class_getInstanceSize ( Class cls ); // 獲取類中指定名稱實(shí)例成員變量的信息 Ivar class_getInstanceVariable ( Class cls, const char *name ); // 獲取類成員變量的信息 Ivar class_getClassVariable ( Class cls, const char *name ); // 獲取指定的屬性 objc_property_t class_getProperty ( Class cls, const char *name ); // 獲取實(shí)例方法 Method class_getInstanceMethod ( Class cls, SEL name ); // 獲取類方法 Method class_getClassMethod ( Class cls, SEL name ); // 獲取方法的具體實(shí)現(xiàn) IMP class_getMethodImplementation ( Class cls, SEL name ); IMP class_getMethodImplementation_stret ( Class cls, SEL name ); | copy: 成員變量列表,;屬性列表,;方法列表;協(xié)議列表,; | // 獲取整個(gè)成員變量列表 Ivar * class_copyIvarList ( Class cls, unsigned int *outCount ); // 獲取屬性列表 objc_property_t * class_copyPropertyList ( Class cls, unsigned int *outCount ); // 獲取所有方法的列表 Method * class_copyMethodList ( Class cls, unsigned int *outCount ); // 獲取類實(shí)現(xiàn)的協(xié)議列表 Protocol * class_copyProtocolList ( Class cls, unsigned int *outCount ); | add: 成員變量,;屬性;方法,;協(xié)議,;(添加成員變量只能在運(yùn)行時(shí)創(chuàng)建的類,且不能為元類) | // 添加成員變量 BOOL class_addIvar ( Class cls, const char *name, size_t size, uint8_t alignment, const char *types ); // 添加屬性 BOOL class_addProperty ( Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount ); // 添加方法 BOOL class_addMethod ( Class cls, SEL name, IMP imp, const char *types ); // 添加協(xié)議 BOOL class_addProtocol ( Class cls, Protocol *protocol ); | replace:屬性,;方法,; | // 替換類的屬性 void class_replaceProperty ( Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount ); // 替代方法的實(shí)現(xiàn) IMP class_replaceMethod ( Class cls, SEL name, IMP imp, const char *types ); | respond:響應(yīng)方法判斷(內(nèi)省) | // 類實(shí)例是否響應(yīng)指定的selector BOOL class_respondsToSelector ( Class cls, SEL sel ); | isMetaClass:元類判斷(內(nèi)?。?/p> | // 判斷給定的Class是否是一個(gè)元類 BOOL class_isMetaClass ( Class cls ); | conform:遵循協(xié)議判斷(內(nèi)?。?/p> | // 返回類是否實(shí)現(xiàn)指定的協(xié)議 BOOL class_conformsToProtocol ( Class cls, Protocol *protocol ); | - objc_:
get: 實(shí)例變量;成員變量,;類名,;類;元類,;關(guān)聯(lián)對(duì)象
| // 獲取對(duì)象實(shí)例變量 Ivar object_getInstanceVariable ( id obj, const char *name, void **outValue ); // 獲取對(duì)象中實(shí)例變量的值 id object_getIvar ( id obj, Ivar ivar ); // 獲取對(duì)象的類名 const char * object_getClassName ( id obj ); // 獲取對(duì)象的類 Class object_getClass ( id obj ); Class objc_getClass ( const char *name ); // 返回指定類的元類 Class objc_getMetaClass ( const char *name ); //獲取關(guān)聯(lián)對(duì)象 id objc_getAssociatedObject(self, &myKey); | copy:對(duì)象,;類,;類列表,;協(xié)議列表; | // 獲取指定對(duì)象的一份拷貝 id object_copy ( id obj, size_t size ); // 創(chuàng)建并返回一個(gè)指向所有已注冊(cè)類的指針列表 Class * objc_copyClassList ( unsigned int *outCount ); | set: 實(shí)例變量,;類,;類列表;協(xié)議,;關(guān)聯(lián)對(duì)象,; | // 設(shè)置類實(shí)例的實(shí)例變量的值 Ivar object_setInstanceVariable ( id obj, const char *name, void *value ); // 設(shè)置對(duì)象中實(shí)例變量的值 void object_setIvar ( id obj, Ivar ivar, id value ); //設(shè)置關(guān)聯(lián)對(duì)象 void objc_setAssociatedObject(self, &myKey, anObject, OBJC_ASSOCIATION_RETAIN); | dispose: 對(duì)象; | // 釋放指定對(duì)象占用的內(nèi)存 id object_dispose ( id obj ); | - 動(dòng)態(tài)創(chuàng)建/銷毀類,、對(duì)象
動(dòng)態(tài)創(chuàng)建/銷毀類:
| // 創(chuàng)建一個(gè)新類和元類 Class objc_allocateClassPair ( Class superclass, const char *name, size_t extraBytes ); // 銷毀一個(gè)類及其相關(guān)聯(lián)的類 void objc_disposeClassPair ( Class cls ); // 在應(yīng)用中注冊(cè)由objc_allocateClassPair創(chuàng)建的類 void objc_registerClassPair ( Class cls ); | 動(dòng)態(tài)創(chuàng)建/銷毀對(duì)象: | // 創(chuàng)建類實(shí)例 id class_createInstance ( Class cls, size_t extraBytes ); // 在指定位置創(chuàng)建類實(shí)例 id objc_constructInstance ( Class cls, void *bytes ); // 銷毀類實(shí)例 void * objc_destructInstance ( id obj ); |
2,、實(shí)例變量、屬性相關(guān): 實(shí)例變量和屬性也是類對(duì)象的關(guān)鍵配置,。
屬性變量的意義就是方便讓其他對(duì)象訪問(wèn)實(shí)例變量,,另外可以拓展實(shí)例變量的作用范圍。當(dāng)然,,你可以設(shè)置只讀或者可寫等,,設(shè)置方法也可自定義,。 a、數(shù)據(jù)類型: Ivar,; | typedef struct objc_ivar *Ivar; struct objc_ivar { char *ivar_name OBJC2_UNAVAILABLE; // 變量名 char *ivar_type OBJC2_UNAVAILABLE; // 變量類型 int ivar_offset OBJC2_UNAVAILABLE; // 基地址偏移字節(jié) #ifdef __LP64__ int space OBJC2_UNAVAILABLE; #endif } | objc_property_t(取名可能是因?yàn)楫?dāng)時(shí)Objective-C1.0還沒(méi)屬性),; | typedef struct objc_property *objc_property_t; | objc_property_attribute_t(屬性的特性有:返回值、是否為atomic,、getter/setter名字,、是否為dynamic、背后使用的ivar名字,、是否為弱引用等),; | typedef struct { const char *name; // 特性名 const char *value; // 特性值 } objc_property_attribute_t; | b、操作函數(shù): | // 獲取成員變量名 const char * ivar_getName ( Ivar v ); // 獲取成員變量類型編碼 const char * ivar_getTypeEncoding ( Ivar v ); // 獲取成員變量的偏移量 ptrdiff_t ivar_getOffset ( Ivar v ); | | // 獲取屬性名 const char * property_getName ( objc_property_t property ); // 獲取屬性特性描述字符串 const char * property_getAttributes ( objc_property_t property ); // 獲取屬性中指定的特性 char * property_copyAttributeValue ( objc_property_t property, const char *attributeName ); // 獲取屬性的特性列表 objc_property_attribute_t * property_copyAttributeList ( objc_property_t property, unsigned int *outCount ); |
3,、 方法消息相關(guān): 消息傳遞機(jī)制是Runtime的核心,,也即消息分派器objc_msgSend。先要知道幾個(gè)概念,。
a,、 數(shù)據(jù)類型:
SEL ; SEL又叫選擇器,,是表示一個(gè)方法的selector的指針,映射方法的名字,。Objective-C在編譯時(shí),會(huì)依據(jù)每一個(gè)方法的名字,、參數(shù)序列,,生成一個(gè)唯一的整型標(biāo)識(shí)(Int類型的地址),這個(gè)標(biāo)識(shí)就是SEL,。 SEL的作用是作為IMP的KEY,,存儲(chǔ)在NSSet中,便于hash快速查詢方法,。SEL不能相同,,對(duì)應(yīng)方法可以不同。所以在Objective-C同一個(gè)類(及類的繼承體系)中,,不能存在2個(gè)同名的方法,,就算參數(shù)類型不同。多個(gè)方法可以有同一個(gè)SEL,。 不同的類可以有相同的方法名,。不同類的實(shí)例對(duì)象執(zhí)行相同的selector時(shí),會(huì)在各自的方法列表中去根據(jù)selector去尋找自己對(duì)應(yīng)的IMP,。 相關(guān)概念:類型編碼(Type Encoding) 編譯器將每個(gè)方法的返回值和參數(shù)類型編碼為一個(gè)字符串,,并將其與方法的selector關(guān)聯(lián)在一起。可以使用@encode編譯器指令來(lái)獲取它,。 | typedef struct objc_selector *SEL; | 中沒(méi)有公開具體的objc_selector結(jié)構(gòu)體成員,。但通過(guò)log可知SEL本質(zhì)是一個(gè)字符串。 IMP ; IMP是指向?qū)崿F(xiàn)函數(shù)的指針,,通過(guò)SEL取得IMP后,,我們就獲得了最終要找的實(shí)現(xiàn)函數(shù)的入口。 | typedefine id (*IMP)(id, SEL, ...) | 這個(gè)結(jié)構(gòu)體相當(dāng)于在SEL和IMP之間作了一個(gè)綁定,。這樣有了SEL,我們便可以找到對(duì)應(yīng)的IMP,,從而調(diào)用方法的實(shí)現(xiàn)代碼,。(在運(yùn)行時(shí)才將SEL和IMP綁定, 動(dòng)態(tài)配置方法) | typedef struct objc_method *Method; struct objc_method { SEL method_name OBJC2_UNAVAILABLE; // 方法名 char *method_types OBJC2_UNAVAILABLE; // 參數(shù)類型 IMP method_imp OBJC2_UNAVAILABLE; // 方法實(shí)現(xiàn) } | objc_method_list 就是用來(lái)存儲(chǔ)當(dāng)前類的方法鏈表,objc_method存儲(chǔ)了類的某個(gè)方法的信息,。 | struct objc_method_list { struct objc_method_list *obsolete OBJC2_UNAVAILABLE; int method_count OBJC2_UNAVAILABLE; #ifdef __LP64__ int space OBJC2_UNAVAILABLE; #endif /* variable length structure */ struct objc_method method_list[1] OBJC2_UNAVAILABLE; } | 方法緩存 ,; 方法調(diào)用最先是在方法緩存里找的,方法調(diào)用是懶調(diào)用,,第一次調(diào)用時(shí)加載后加到緩存池里,。一個(gè)objc程序啟動(dòng)后,需要進(jìn)行類的初始化,、調(diào)用方法時(shí)的cache初始化,,再發(fā)送消息的時(shí)候就直接走緩存(引申:+load方法和+initialize方法。load方法是首次加載類時(shí)調(diào)用,,絕對(duì)只調(diào)用一次,;initialize方法是首次給類發(fā)消息時(shí)調(diào)用,通常只調(diào)用一次,,但如果它的子類初始化時(shí)未定義initialize方法,,則會(huì)再調(diào)用一次它的initialize方法)。 | struct objc_cache { // 緩存bucket的總數(shù) unsigned int mask /* total = mask + 1 */ OBJC2_UNAVAILABLE; // 實(shí)際緩存bucket的總數(shù) unsigned int occupied OBJC2_UNAVAILABLE; // 指向Method數(shù)據(jù)結(jié)構(gòu)指針的數(shù)組 Method buckets[1] OBJC2_UNAVAILABLE; }; | b,、 操作函數(shù): - method_:
invoke: 方法實(shí)現(xiàn)的返回值,;
| // 調(diào)用指定方法的實(shí)現(xiàn) id method_invoke ( id receiver, Method m, ... ); // 調(diào)用返回一個(gè)數(shù)據(jù)結(jié)構(gòu)的方法的實(shí)現(xiàn) void method_invoke_stret ( id receiver, Method m, ... ); | get: 方法名;方法實(shí)現(xiàn),;參數(shù)與返回值相關(guān); | // 獲取方法名 SEL method_getName ( Method m ); // 返回方法的實(shí)現(xiàn) IMP method_getImplementation ( Method m ); // 獲取描述方法參數(shù)和返回值類型的字符串 const char * method_getTypeEncoding ( Method m ); // 返回方法的參數(shù)的個(gè)數(shù) unsigned int method_getNumberOfArguments ( Method m ); // 通過(guò)引用返回方法指定位置參數(shù)的類型字符串 void method_getArgumentType ( Method m, unsigned int index, char *dst, size_t dst_len ); | copy: 返回值類型,,參數(shù)類型 | // 獲取方法的返回值類型的字符串 char * method_copyReturnType ( Method m ); // 獲取方法的指定位置參數(shù)的類型字符串 char * method_copyArgumentType ( Method m, unsigned int index ); // 通過(guò)引用返回方法的返回值類型字符串 void method_getReturnType ( Method m, char *dst, size_t dst_len ); | set:方法實(shí)現(xiàn),; | // 設(shè)置方法的實(shí)現(xiàn) IMP method_setImplementation ( Method m, IMP imp ); | exchange:交換方法實(shí)現(xiàn) | // 交換兩個(gè)方法的實(shí)現(xiàn) void method_exchangeImplementations ( Method m1, Method m2 ); | description : 方法描述 | // 返回指定方法的方法描述結(jié)構(gòu)體 struct objc_method_description * method_getDescription ( Method m ); | | // 返回給定選擇器指定的方法的名稱 const char * sel_getName ( SEL sel ); // 在Objective-C Runtime系統(tǒng)中注冊(cè)一個(gè)方法,將方法名映射到一個(gè)選擇器,,并返回這個(gè)選擇器 SEL sel_registerName ( const char *str ); // 在Objective-C Runtime系統(tǒng)中注冊(cè)一個(gè)方法 SEL sel_getUid ( const char *str ); // 比較兩個(gè)選擇器 BOOL sel_isEqual ( SEL lhs, SEL rhs ); | c,、方法調(diào)用流程 :向?qū)ο蟀l(fā)送消息,實(shí)際上是調(diào)用objc_msgSend函數(shù),,obj_msgSend的實(shí)際動(dòng)作就是:找到這個(gè)函數(shù)指針,,然后調(diào)用它,。 | id objc_msgSend(receiver self, selector _cmd, arg1, arg2, ...) | self和_cmd是隱藏參數(shù),在編譯期被插入實(shí)現(xiàn)代碼,。 self:指向消息的接受者target的對(duì)象類型,,作為一個(gè)占位參數(shù),消息傳遞成功后self將指向消息的receiver,。 _cmd: 指向方法實(shí)現(xiàn)的SEL類型,。 當(dāng)向一般對(duì)象發(fā)送消息時(shí),調(diào)用objc_msgSend,;當(dāng)向super發(fā)送消息時(shí),,調(diào)用的是objc_msgSendSuper; 如果返回值是一個(gè)結(jié)構(gòu)體,,則會(huì)調(diào)用objc_msgSend_stret或objc_msgSendSuper_stret,。 0.1-檢查target是否為nil。如果為nil,,直接cleanup,,然后return。(這就是我們可以向nil發(fā)送消息的原因,。) 如果方法返回值是一個(gè)對(duì)象,,那么發(fā)送給nil的消息將返回nil;如果方法返回值為指針類型,,其指針大小為小于或者等于sizeof(void*),,float,double,,long double 或者long long的整型標(biāo)量,,發(fā)送給nil的消息將返回0;如果方法返回值為結(jié)構(gòu)體,發(fā)送給nil的消息將返回0,。結(jié)構(gòu)體中各個(gè)字段的值將都是0,;如果方法的返回值不是上述提到的幾種情況,那么發(fā)送給nil的消息的返回值將是未定義的,。 0.2-如果target非nil,,在target的Class中根據(jù)Selector去找IMP。(因?yàn)橥粋€(gè)方法可能在不同的類中有不同的實(shí)現(xiàn),,所以我們需要依賴于接收者的類來(lái)找到的確切的實(shí)現(xiàn)),。
1-首先它找到selector對(duì)應(yīng)的方法實(shí)現(xiàn): *1.1-在target類的方法緩存列表里檢查有沒(méi)有對(duì)應(yīng)的方法實(shí)現(xiàn),有的話,,直接調(diào)用,。 *1.2-比較請(qǐng)求的selector和類方法列表中的selector,對(duì)應(yīng)的話,直接調(diào)用,。 *1.3-比較請(qǐng)求的selector和父類方法列表,,父類的父類,直至根類,,如果有對(duì)應(yīng),,則直接調(diào)用。(方法重寫攔截父類方法的原理) 2-調(diào)用方法實(shí)現(xiàn),,并將接收者對(duì)象及方法的所有參數(shù)傳給它,。 3-最后,將實(shí)現(xiàn)函數(shù)的返回值作為自己的返回值,。
d,、動(dòng)態(tài)方法解析與消息轉(zhuǎn)發(fā) :如果以上的類中沒(méi)有找到對(duì)應(yīng)的selector(一般保險(xiǎn)起見先用respondsToSelector:內(nèi)省判斷):,還可以利用消息轉(zhuǎn)發(fā)機(jī)制依次執(zhí)行以下流程: - Method Resolution(動(dòng)態(tài)方法解析):
用所屬類的類方法+(BOOL)resolveInstanceMethod:(實(shí)例方法)或者+(BOOL)resolveClassMethod:(類方法),在此方法里添加class_addMethod函數(shù),。一般用于@dynamic動(dòng)態(tài)屬性,。(當(dāng)一個(gè)屬性聲明為@dynamic,就是向編譯器保證編譯時(shí)不用管/get實(shí)現(xiàn),,一定會(huì)在運(yùn)行時(shí)實(shí)現(xiàn)),。 - Fast Forwarding (快速消息轉(zhuǎn)發(fā)):
如果上一步無(wú)法響應(yīng)消息,調(diào)用- (id)forwardingTargetForSelector:(SEL)aSelector方法,,將消息接受者轉(zhuǎn)發(fā)到另一個(gè)對(duì)象target(不能為self,,否則死循環(huán))。 - Normal Forwarding(普通消息轉(zhuǎn)發(fā)):
如果上一步無(wú)法響應(yīng)消息: 調(diào)用方法簽名- (NSMethodSignature )methodSignatureForSelector:(SEL)aSelector,,方法簽名目的將函數(shù)的參數(shù)類型和返回值封裝,; 如果返回非nil,則創(chuàng)建一個(gè)NSInvocation對(duì)象利用方法簽名和selector封裝未被處理的消息,,作為參數(shù)傳遞給- (void)forwardInvocation:(NSInvocation )anInvocation,。 這一步比較耗時(shí)。
如果以上步驟(消息傳遞和消息轉(zhuǎn)發(fā))還是不能響應(yīng)消息,,則調(diào)動(dòng)doesNotRecognizeSelector:方法,,拋出異常。 | unrecognized selector sent to instance | (消息轉(zhuǎn)發(fā)可以利用轉(zhuǎn)移消息接受對(duì)象,,實(shí)現(xiàn)偽多重繼承的效果,。)
4、 協(xié)議相關(guān):@protocol聲明了可以被其他任何類實(shí)現(xiàn)的方法,,協(xié)議僅僅是定義一個(gè)接口,,而由其他的類去負(fù)責(zé)實(shí)現(xiàn)。
數(shù)據(jù)類型:Protocol,; | typedef struct objc_object Protocol; | protocol是一個(gè)對(duì)象結(jié)構(gòu)體。 操作函數(shù): | // 返回指定的協(xié)議 Protocol * objc_getProtocol ( const char *name ); // 獲取運(yùn)行時(shí)所知道的所有協(xié)議的數(shù)組 Protocol ** objc_copyProtocolList ( unsigned int *outCount ); // 創(chuàng)建新的協(xié)議實(shí)例 Protocol * objc_allocateProtocol ( const char *name ); // 在運(yùn)行時(shí)中注冊(cè)新創(chuàng)建的協(xié)議 void objc_registerProtocol ( Protocol *proto ); | - protocol_:
get: 協(xié)議;屬性,;
| // 返回協(xié)議名 const char * protocol_getName ( Protocol *p ); // 獲取協(xié)議的指定屬性 objc_property_t protocol_getProperty ( Protocol *proto, const char *name, BOOL isRequiredProperty, BOOL isInstanceProperty ); | copy:協(xié)議列表,;屬性列表; | // 獲取協(xié)議中的屬性列表 objc_property_t * protocol_copyPropertyList ( Protocol *proto, unsigned int *outCount ); // 獲取協(xié)議采用的協(xié)議 Protocol ** protocol_copyProtocolList ( Protocol *proto, unsigned int *outCount ); | add:屬性,;方法,;協(xié)議; | // 為協(xié)議添加方法 void protocol_addMethodDescription ( Protocol *proto, SEL name, const char *types, BOOL isRequiredMethod, BOOL isInstanceMethod ); // 添加一個(gè)已注冊(cè)的協(xié)議到協(xié)議中 void protocol_addProtocol ( Protocol *proto, Protocol *addition ); // 為協(xié)議添加屬性 void protocol_addProperty ( Protocol *proto, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount, BOOL isRequiredProperty, BOOL isInstanceProperty ); | | // 測(cè)試兩個(gè)協(xié)議是否相等 BOOL protocol_isEqual ( Protocol *proto, Protocol *other ); | comform:判斷是否遵循協(xié)議,; | // 查看協(xié)議是否采用了另一個(gè)協(xié)議 BOOL protocol_conformsToProtocol ( Protocol *proto, Protocol *other ); |
5、 其他:類名,;版本號(hào),;類信息;(忽略)
三,、 動(dòng)態(tài)實(shí)現(xiàn): - Method Swizzling;
Method Swizzling可以在運(yùn)行時(shí)通過(guò)修改類的方法列表中selector對(duì)應(yīng)的函數(shù)或者設(shè)置交換方法實(shí)現(xiàn),,來(lái)動(dòng)態(tài)修改方法??梢灾貙懩硞€(gè)方法而不用繼承,,同時(shí)還可以調(diào)用原先的實(shí)現(xiàn)。通常應(yīng)用于在category中添加一個(gè)方法,。 為保證改變方法引起沖突,,確保方法混用只能一次性: 比如,在+load方法或者dispatch_once中執(zhí)行,。 - ISA Swizzling,;
ISA Swizzling可以動(dòng)態(tài)修改對(duì)象的isa指針,改變對(duì)象的類,,類似于創(chuàng)建子類實(shí)現(xiàn)相同的功能,。KVO即是同過(guò)ISA Swizzling實(shí)現(xiàn)的。
四,、 其他概念:category,;super;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | typedef struct objc_category *Category; struct objc_category { char *category_name OBJC2_UNAVAILABLE; // 分類名 char *class_name OBJC2_UNAVAILABLE; // 分類所屬的類名 struct objc_method_list *instance_methods OBJC2_UNAVAILABLE; // 實(shí)例方法列表 struct objc_method_list *class_methods OBJC2_UNAVAILABLE; // 類方法列表 struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 分類所實(shí)現(xiàn)的協(xié)議列表 } // objc-runtime-new.h中定義: struct category_t { const char *name; // name 是指 class_name 而不是 category_name classref_t cls; // cls是要擴(kuò)展的類對(duì)象,,編譯期間是不會(huì)定義的,,而是在Runtime階段通過(guò)name對(duì)應(yīng)到對(duì)應(yīng)的類對(duì)象 struct method_list_t *instanceMethods; struct method_list_t *classMethods; struct protocol_list_t *protocols; struct property_list_t *instanceProperties; // instanceProperties表示Category里所有的properties,(這就是我們可以通過(guò)objc_setAssociatedObject和objc_getAssociatedObject增加實(shí)例變量的原因,,)不過(guò)這個(gè)和一般的實(shí)例變量是不一樣的 }; | category就是定義方法的結(jié)構(gòu)體,,instance_methods列表是objc_class中方法列表的一個(gè)子集,class_methods列表是元類方法列表的一個(gè)子集,。由其結(jié)構(gòu)成員可知,,category為什么不能添加成員變量(可添加屬性,,只有set/get方法)。 給category添加方法后,,category_list會(huì)生成method list,。這個(gè)方法列表是倒序添加的,也就是說(shuō),,新生成的category的方法會(huì)先于舊的category的方法插入,。(category的方法會(huì)優(yōu)先于類方法執(zhí)行)。 super并不是隱藏參數(shù),,它實(shí)際上只是一個(gè)”編譯器標(biāo)示符”,,它負(fù)責(zé)告訴編譯器,當(dāng)調(diào)用方法時(shí),,跳過(guò)當(dāng)前類去調(diào)用父類的方法,,而不是本類中的方法。self是類的一個(gè)隱藏參數(shù),,每個(gè)方法的實(shí)現(xiàn)的第一個(gè)參數(shù)即為self,。實(shí)際上給super發(fā)消息時(shí),super還是與self指向的是相同的消息接收者,。 | struct objc_super { __unsafe_unretained id receiver; __unsafe_unretained Class super_class; }; | 原理:使用super來(lái)接收消息時(shí),,編譯器會(huì)生成一個(gè)objc_super結(jié)構(gòu)體。發(fā)送消息時(shí),,不是調(diào)用objc_msgSend函數(shù),,而是調(diào)用objc_msgSendSuper函數(shù): | id objc_msgSendSuper ( struct objc_super *super, SEL op, ... ); | 該函數(shù)實(shí)際的操作是:從objc_super結(jié)構(gòu)體指向的superClass的方法列表開始查找selector,找到后以objc->receiver去調(diào)用這個(gè)selector,。 - Runtime開源源碼對(duì)一些方法的實(shí)現(xiàn):
| - (Class)class ; - (Class)class { return object_getClass(self); } | | + (Class)class; + (Class)class { return self; } | | - (BOOL)isKindOf:aClass;// (for循環(huán)遍歷父類,,每次判斷返回的結(jié)果可能不同) - (BOOL)isKindOf:aClass { Class cls; for (cls = isa; cls; cls = cls->superclass) if (cls == (Class)aClass) return YES; return NO; } | | - (BOOL)isMemberOf:aClass; - (BOOL)isMemberOf:aClass { return isa == (Class)aClass; } | 加入伯樂(lè)在線專欄作者。擴(kuò)大知名度,,還能得贊賞,!詳見《 招募專欄作者》
|