runtime詳解

運(yùn)行時(shí)是iOS中一個(gè)很重要的概念,iOS運(yùn)行過程中都會(huì)被轉(zhuǎn)化為runtime的C代碼執(zhí)行。例如[target doSomething];會(huì)被轉(zhuǎn)化成objc)msgSend(target,@selector(doSomething))來執(zhí)行。這篇博客會(huì)較為全面的來講解下Runtime。

OC是一門動(dòng)態(tài)語(yǔ)言,它將很多靜態(tài)語(yǔ)言在編譯和鏈接時(shí)做的事放到了運(yùn)行時(shí)來處理。這種動(dòng)態(tài)語(yǔ)言的優(yōu)勢(shì)在于:寫代碼能更加靈活,可以把消息轉(zhuǎn)發(fā)給想要的對(duì)象,或者隨意交換一個(gè)方法的實(shí)現(xiàn)。

OC Runtime目前有兩個(gè)版本:Modern Runtime和Legacy Runtime。Modern Runtime覆蓋了64位的App,Legacy Runtime使用早期的32位App,所以現(xiàn)在可以不用管了。

(1)當(dāng)我們需要使用Runtime的接口時(shí),需要導(dǎo)入頭文件:#import ,Runtime可以進(jìn)行如下操作,在運(yùn)行時(shí)來獲取當(dāng)前類中的一些信息:

獲取屬性列表:

獲取方法列表:

獲取成員變量列表:

獲取協(xié)議列表:

所以,我們大概可以總結(jié)出Runtime的作用:

程序運(yùn)行中,動(dòng)態(tài)創(chuàng)建一個(gè)類;

程序運(yùn)行中,動(dòng)態(tài)為某個(gè)類添加屬性/方法,修改屬性/方法;

遍歷一個(gè)類的所有成員變量/屬性/方法;

(2)方法調(diào)用:

如果用實(shí)例對(duì)象調(diào)用實(shí)例方法,會(huì)到實(shí)例的isa指針指向的對(duì)象(也就是類對(duì)象,沒錯(cuò),其實(shí)類也是一個(gè)對(duì)象)操作。如果調(diào)用的是類方法,就會(huì)到類的isa指針指向的對(duì)象(元類對(duì)象)中操作。查找過程如下:

首先在相應(yīng)操作的對(duì)象中的緩存方法列表中找調(diào)用的方法,如果找到,轉(zhuǎn)向相應(yīng)實(shí)現(xiàn)并執(zhí)行;

如果沒有找到,在相應(yīng)操作的對(duì)象中的方法列表中找調(diào)用的方法,如果找到,轉(zhuǎn)向相應(yīng)實(shí)現(xiàn)執(zhí)行;

如果沒找到,去父類指針?biāo)赶虻膶?duì)象中執(zhí)行以上步驟;

以此類推,如果一直到根類還沒有找到,轉(zhuǎn)向攔截調(diào)用;

如果沒有重寫攔截調(diào)用的方法,程序報(bào)錯(cuò);

說明下,重寫父類的方法,其實(shí)并沒有覆蓋掉父類的方法,只是在當(dāng)前類對(duì)象中找到這個(gè)方法后就不會(huì)再去父類中找了。如果想調(diào)用已經(jīng)重寫過的方法的父類實(shí)現(xiàn),只需使用super這個(gè)編譯器標(biāo)志,它會(huì)在運(yùn)行時(shí)跳過在當(dāng)前類對(duì)象中尋找方法的過程。

(3)攔截調(diào)用

攔截調(diào)用就是在找不到調(diào)用的方法程序崩潰之前,有機(jī)會(huì)通過重寫NSObject的四個(gè)方法來處理:

以下兩個(gè)方法需要轉(zhuǎn)發(fā)到其他類處理:

第一個(gè)方法是調(diào)用一個(gè)不存在的類方法的時(shí)候,會(huì)調(diào)用這個(gè)方法,默認(rèn)返回NO,可以加上自己的處理后返回YES;

第二個(gè)方法和第一個(gè)方法類似,處理的是實(shí)例方法;

第三個(gè)方法是將你調(diào)用的不存在的方法重定向到一個(gè)其他聲明了這個(gè)方法的類,只需要返回一個(gè)有這個(gè)方法的target;

第四個(gè)方法是將你調(diào)用的不存在的方法打包成NSInvocation傳給你。做完你自己的處理后,調(diào)用invokeWithTarget:方法讓某個(gè)target觸發(fā)這個(gè)方法;

(4)Runtime的常用之處就是可以在運(yùn)行時(shí)動(dòng)態(tài)添加方法。我們?cè)谏厦嬷v到了攔截調(diào)用,動(dòng)態(tài)添加方法可以和攔截調(diào)用結(jié)合起來使用。我們可以根據(jù)傳遞進(jìn)來的SEL類型的selector動(dòng)態(tài)添加一個(gè)方法。這樣就算我們調(diào)用一個(gè)還沒定義的方法程序也不會(huì)crash了。

.

其中class_addMethod的四個(gè)參數(shù)分別是:

Class cls :給哪個(gè)類添加方法;

SEL name:方法選擇器selector;

IMP imp:方法的實(shí)現(xiàn),C方法的實(shí)現(xiàn)可以直接獲得。如果是OC方法,可以用+(IMP)instanceMethodForSelector獲得方法的實(shí)現(xiàn);

const char *types:方法的簽名

(5)關(guān)聯(lián)對(duì)象

現(xiàn)在準(zhǔn)備使用一個(gè)系統(tǒng)的類,你需要額外添加一個(gè)屬性。我們可以使用繼承。但是只增加一個(gè)屬性,就去繼承一個(gè)類,是比較浪費(fèi)的。這時(shí)我們就可以使用Runtime的關(guān)聯(lián)屬性。

先定義一個(gè)全局變量,用它的地址作為關(guān)聯(lián)對(duì)象的key:

設(shè)置關(guān)聯(lián)對(duì)象:

objc_setAssociatedObject的四個(gè)參數(shù):

id object:給誰(shuí)設(shè)置關(guān)聯(lián)對(duì)象;

const void *key:關(guān)聯(lián)對(duì)象唯一的key,就是上面定義的全局變量;

id value:關(guān)聯(lián)對(duì)象;

objc_AssociatedPolicy:關(guān)聯(lián)策略:

objc_getAssociatedObject的兩個(gè)參數(shù):

id object:獲取誰(shuí)的關(guān)聯(lián)對(duì)象;

const void *key:根據(jù)這個(gè)唯一的key獲取關(guān)聯(lián)對(duì)象;

(6)方法交換

將兩個(gè)方法的實(shí)現(xiàn)交換。例如,將A方法和B方法交換,調(diào)用A方法的時(shí)候,就會(huì)執(zhí)行B方法中的代碼。反之亦然。我們一般在類的Category中實(shí)現(xiàn)以下代碼:

(7)Runtime原理與機(jī)制

Runtime運(yùn)行時(shí),就是系統(tǒng)在運(yùn)行的時(shí)候的一些機(jī)制,其中最主要的是消息機(jī)制。同時(shí)Runtime是一套比較底層的C語(yǔ)言API,屬于一個(gè)C語(yǔ)言庫(kù),包含了很多底層的C語(yǔ)言API。程序運(yùn)行時(shí),最終都是轉(zhuǎn)成了Runtime的C語(yǔ)言代碼。

OC實(shí)現(xiàn)動(dòng)態(tài)調(diào)用:

[obj makeText];

在編譯時(shí)Runtime會(huì)將代碼轉(zhuǎn)化為:

objc_msgSend(obj,@selector(makeText));

obj對(duì)象有一個(gè)isa指針,

所有metaclass中isa指針都指向根metaclass,而根metaclass則指向自身。Root metaclass是通過繼承Root Class產(chǎn)生的,與root class結(jié)構(gòu)體成員一致。

@selector(makeText):這是一個(gè)SEL方法選擇器。SEL主要作用是快速通過方法名字(makeText)查找到對(duì)應(yīng)方法的函數(shù)指針,然后調(diào)用函數(shù)。SEL本身是一個(gè)int類型的地址,地址中存放著方法的名字。對(duì)于一個(gè)類中,每一個(gè)方法對(duì)應(yīng)著一個(gè)SEL,所以iOS中不能存在2個(gè)名稱相同的方法,即使參數(shù)類型不同。

動(dòng)態(tài)查找過程:

編譯器將代碼[obj makeText];轉(zhuǎn)化為objc_msgSend(obj,@selector(makeText));在objc_msgSend函數(shù)中,首先通過obj的isa指針找到obj對(duì)應(yīng)的class。在Class中先去cache中通過SEL查找對(duì)應(yīng)函數(shù)method,若cache中未找到,再去methodList中查找,若methodList中未找到,則到superClass查找。若能找到,則將method加入到cache中,以方便下次查找,并通過method中的函數(shù)指針跳轉(zhuǎn)到對(duì)應(yīng)的函數(shù)中去執(zhí)行。

注意:上面提到為什么要先去查找緩存cache?

因?yàn)楫?dāng)調(diào)用了一個(gè)方法,很有可能之后還會(huì)調(diào)用它。軟件開發(fā)的同學(xué)應(yīng)該都可以理解。

(8)OC中的類

OC類是由Class類型來表示的,實(shí)際上是一個(gè)指向objc_class結(jié)構(gòu)體的指針,Class定義在objc.h中:

蘋果注釋中也說明了,Class是一個(gè)隱含類型代表了一個(gè)OC類。查看runtime.h中關(guān)于objc_class結(jié)構(gòu)體的定義:

注釋中也說明,以后使用Class來替代struct objc_class,這樣就可以方便類型定義。

version:類的版本信息,默認(rèn)為0;

info:類信息,運(yùn)行時(shí)使用的一些位標(biāo)志;

instance_size:類的實(shí)例變量的大小;

其中我們最關(guān)心的參數(shù)是:

isa:所有的類自身也是一個(gè)對(duì)象,這個(gè)對(duì)象的Class里面也有一個(gè)isa指針,它指向metaClass元類;

super_class:指向該類的父類,如果該類已經(jīng)是最頂層的根類(如NSObject或NSProxy),則super_class為NULL;

(9)類的實(shí)例

objc_object表示的是一個(gè)類的實(shí)例的結(jié)構(gòu)體,在objc.h中定義:

注釋中也說明了,objc_object代表了類的實(shí)例。然后使用id來指向這個(gè)類的實(shí)例結(jié)構(gòu)體,同樣在objc_h中定義:

一個(gè)指向?qū)嵗闹羔槨?梢钥吹皆诮Y(jié)構(gòu)體中只有一個(gè)字段,就是指向其類的isa指針。這樣,當(dāng)我們向一個(gè)OC對(duì)象發(fā)送消息時(shí),運(yùn)行時(shí)庫(kù)會(huì)根據(jù)實(shí)例對(duì)象的isa指針找到這個(gè)實(shí)例對(duì)象所屬的類。Runtime庫(kù)會(huì)在類的方法列表及父類的方法列表中去尋找與消息對(duì)應(yīng)的selector指向的方法,找到后即運(yùn)行這個(gè)方法。

id:是一個(gè)objc_object結(jié)構(gòu)類型的指針,可以轉(zhuǎn)換為任何一種對(duì)象,類似C中的void *.

(10)元類MetaClass

所有的類自身也是一個(gè)對(duì)象,可以向這個(gè)對(duì)象發(fā)送消息(即調(diào)用類方法)。

NSArray *array = [NSArray array];

+array消息發(fā)送給了NSArray類,而這個(gè)NSArray也是一個(gè)對(duì)象。既然是對(duì)象,那么它也是一個(gè)objc_object指針,包含一個(gè)指向其類的一個(gè)isa指針。為了調(diào)用+array方法,這個(gè)類的isa指針必須指向一個(gè)包含這些類方法的一個(gè)objc_class結(jié)構(gòu)體。這就需要meta_class概念。

meta_class是一個(gè)類對(duì)象的類。當(dāng)我們向一個(gè)對(duì)象發(fā)送消息時(shí),Runtime會(huì)在這個(gè)對(duì)象所屬的這個(gè)類的方法列表中查找方法。而向一個(gè)類發(fā)送消息時(shí),會(huì)在這個(gè)類的meta_class的方法列表中查找。meta_class很重要,因?yàn)樗鎯?chǔ)著一個(gè)類的所有的類方法。每個(gè)類都會(huì)有一個(gè)單獨(dú)的meta_class,因?yàn)槊總€(gè)類的類方法基本不可能完全相同。

再看一下上面提到的圖,現(xiàn)在應(yīng)該有更深的理解:

所有的meta_class的isa指向基類的meta_class,依次作為他們的所屬類。即任何NSObject繼承體系下的meta_class都使用NSObject的meta_class作為自己的所屬類,而基類的meta_class的isa指針指向自己,這樣就形成了閉環(huán)。

(11)類與對(duì)象操作函數(shù)

類的操作方法大部分是以class為前綴的,而對(duì)象的操作方法大部分是以objc或object_為前綴的。

類名

返回一個(gè)類的名字,傳入的參數(shù)是類對(duì)象。

父類(super_class)和元類(meta_class)

獲取類的父類:

判斷給定的Class是否是一個(gè)元類:

獲取實(shí)例變量的大小:

在objc_class中,所有的成員變量,屬性的信息是放在鏈表ivars中的,ivars是一個(gè)數(shù)組,數(shù)組中每個(gè)元素是指向Ivar的指針。

獲取類中實(shí)例成員變量的信息

獲取類成員變量的信息

添加成員變量

獲取成員變量列表

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

以下數(shù)屬性操作函數(shù)

獲取指定的屬性

獲取屬性列表

為類添加屬性

替換類的屬性

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

以下為方法操作函數(shù):

添加方法

獲取實(shí)例方法

獲取類方法

獲取所有方法的數(shù)組

替換方法的實(shí)現(xiàn)

返回方法的具體實(shí)現(xiàn)

類實(shí)例是否響應(yīng)指定的selector

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

協(xié)議操作函數(shù)如下:

添加協(xié)議

返回類是否實(shí)現(xiàn)指定的協(xié)議

返回類實(shí)現(xiàn)的協(xié)議列表

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

獲取版本Version如下:

獲取版本號(hào)

設(shè)置版本號(hào)

(12)動(dòng)態(tài)創(chuàng)建類

創(chuàng)建一個(gè)新類和元類

銷毀一個(gè)類及其相關(guān)聯(lián)的類

在應(yīng)用中注冊(cè)由objc_allocateClassPair創(chuàng)建的類

objc_allocateClassPair函數(shù):如果要?jiǎng)?chuàng)建一個(gè)根類,則superclass指定為nil,extraBytes通常指定為0,該參數(shù)是分配給類和元類對(duì)象尾部的索引ivars的字節(jié)數(shù)。然后使用諸如class_addMethod,class_addIvar等函數(shù)來為新創(chuàng)建的類添加方法,實(shí)例變量和屬性。完成這些后,需要調(diào)用objc_registerClassPair函數(shù)來注冊(cè)類,之后這個(gè)新類就可以在程序中使用了。

注意:實(shí)例方法和實(shí)例變量應(yīng)該添加到類自身上,類方法應(yīng)該添加到類的元類上。

(13)動(dòng)態(tài)創(chuàng)建對(duì)象

創(chuàng)建類實(shí)例

在指定位置創(chuàng)建類實(shí)例

銷毀類實(shí)例

(14)實(shí)例操作函數(shù)

實(shí)例操作函數(shù)主要是針對(duì)我們創(chuàng)建的實(shí)例對(duì)象的一系列操作函數(shù),我們可以使用這組函數(shù)來從實(shí)例對(duì)象中獲取我們要的信息,如實(shí)例對(duì)象中變量的值。

返回指定對(duì)象的一份拷貝

釋放指定對(duì)象占用的內(nèi)存

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------

針對(duì)對(duì)象實(shí)例變量進(jìn)行操作的函數(shù)

修改類實(shí)例的實(shí)例變量的值

獲取類對(duì)象中實(shí)例變量

返回對(duì)象中實(shí)例變量的值

設(shè)置對(duì)象中實(shí)例變量的值

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------

針對(duì)對(duì)象的類進(jìn)行操作的函數(shù)

返回給定對(duì)象的類名

返回對(duì)象的類

設(shè)置對(duì)象的類

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------

獲取類定義:Runtime會(huì)自動(dòng)注冊(cè)我們代碼中定義的所有類,我們也可以在運(yùn)行時(shí)創(chuàng)建類定義并使用objc_addClass函數(shù)來注冊(cè)他它們。

獲取已注冊(cè)的類定義的列表

創(chuàng)建并返回一個(gè)指向所有已注冊(cè)類的指針列表

返回指定類的類定義

返回指定類的元類

(15)成員變量、屬性

Ivar

Ivar是表示實(shí)例變量的類型,實(shí)際是一個(gè)指向objc_ivar結(jié)構(gòu)體的指針。

objc_property_t

objc_property_t是表示OC聲明的屬性的類型,實(shí)際是指向objc_property結(jié)構(gòu)體的指針。

objc_property_attribute_t

定義了屬性的特性,是一個(gè)結(jié)構(gòu)體。

(16)關(guān)聯(lián)對(duì)象

關(guān)聯(lián)對(duì)象類似于成員變量,不過是在運(yùn)行時(shí)添加的。通常會(huì)把成員變量Ivar放在類聲明的頭文件中,或者放在類實(shí)現(xiàn)的@implementation后面。但是有一個(gè)缺點(diǎn),不能在Category中添加成員變量,否則會(huì)編譯器報(bào)錯(cuò)。可以把關(guān)聯(lián)對(duì)象想象成一個(gè)OC對(duì)象(如字典),這個(gè)對(duì)象通過給定的key連接到類的一個(gè)實(shí)例上。由于使用的是C接口,所以key是一個(gè)void指針(const void *). 其中使用以下的內(nèi)存管理策略來管理這個(gè)對(duì)象。同上面(5)

(17)SEL

SEL叫選擇器,是表示一個(gè)方法的selector指針,定義如下:

objc_selector結(jié)構(gòu)體的詳細(xì)定義沒有在頭文件中找到,方法的selector用于表示運(yùn)行時(shí)方法的名字。OC在編譯時(shí),會(huì)根據(jù)每一個(gè)方法的名字生成一個(gè)唯一的整型標(biāo)志(int類型的地址),這個(gè)標(biāo)示就是SEL。

代碼所示:

打印結(jié)果如下:

兩個(gè)類之間,如果是繼承關(guān)系,只要方法名相同,那么方法的SEL就是一樣的。每一個(gè)方法對(duì)應(yīng)一個(gè)SEL。所以在OC的同一個(gè)類中(或類的繼承體系中),不能存在兩個(gè)同名的方法,即使參數(shù)類型不同也不行。所以說,OC不存在重載的概念。當(dāng)然,不同的類可以擁有相同的selector。不同類的實(shí)例對(duì)象執(zhí)行相同的selector時(shí),會(huì)在各自的方法列表中去根據(jù)selector尋找自己對(duì)應(yīng)的IMP。

工程中所有的SEL組成了一個(gè)Set集合,Set的特點(diǎn)就是唯一,因此SEL是唯一的。因此,如果我們想到這個(gè)方法集合中查找某個(gè)方法時(shí),只需要找到這個(gè)方法對(duì)應(yīng)的SEL即可。SEL實(shí)際上就是根據(jù)方法名hash了一個(gè)字符串,而對(duì)于字符串的比較僅僅需要比較他們的地址就可以了。本質(zhì)上,SEL只是一個(gè)指向方法的指針。

(18)IMP

IMP實(shí)際上是一個(gè)函數(shù)指針,指向方法實(shí)現(xiàn)的首地址。定義如下:

第一個(gè)參數(shù)是指向self的指針(如果是實(shí)例方法,則是類實(shí)例的內(nèi)存地址;如果是類方法,則是指向元類的指針),第二個(gè)參數(shù)是方法選擇器(selector),接下來是方法的實(shí)際參數(shù)列表。

(19)Method

看到在結(jié)構(gòu)體中有SEL和IMP,相當(dāng)于在SEL和IMP之間做了一個(gè)映射。有了SEL,就可以找到對(duì)應(yīng)的IMP,從而調(diào)用方法的實(shí)現(xiàn)代碼。

objc_method_description定義了一個(gè)OC方法,定義如下:

(20)方法相關(guān)操作函數(shù)

調(diào)用指定方法的實(shí)現(xiàn)

獲取方法名

返回方法的實(shí)現(xiàn)

獲取描述方法參數(shù)和返回值類型的字符串

獲取方法指定位置參數(shù)的類型字符串

通過引用返回方法的返回值類型字符串

返回方法的參數(shù)個(gè)數(shù)

返回指定方法的方法描述結(jié)構(gòu)體

設(shè)置方法的實(shí)現(xiàn)

交換兩個(gè)方法的實(shí)現(xiàn)

(21)方法選擇器

返回給定選擇器指定的方法的名稱

Runtime中注冊(cè)一個(gè)方法,將方法名映射到一個(gè)選擇器,并返回這個(gè)選擇器。在我們將一個(gè)方法添加到類定義時(shí),必須在Runtime中注冊(cè)一個(gè)方法名以獲取方法的選擇器。

在Runtime中注冊(cè)一個(gè)方法

比較兩個(gè)選擇器

(22)方法調(diào)用流程

在OC中,消息直到運(yùn)行時(shí)才綁定到方法的實(shí)現(xiàn)上。編譯器會(huì)將消息表達(dá)式[receive message]轉(zhuǎn)化為一個(gè)消息函數(shù)的調(diào)用,即objc_msgSend。這個(gè)函數(shù)將消息接收者和方法名作為基礎(chǔ)參數(shù),如下所示:

可以看到,第三個(gè)參數(shù)是個(gè)可變參數(shù),用來傳遞原方法的參數(shù)列表:

objc_msg_Send(receive,selector,arg1,arg2).這個(gè)函數(shù)完成了動(dòng)態(tài)綁定的所有事情。

首先它找到selector對(duì)應(yīng)的方法實(shí)現(xiàn)。因?yàn)橥粋€(gè)方法可能在不同的類中有不同的實(shí)現(xiàn),所以我們需要依賴接收者的類來找到確切的實(shí)現(xiàn)。

它調(diào)用方法的實(shí)現(xiàn),并將接收者對(duì)象及方法的所有參數(shù)傳給它。

最后,它將實(shí)現(xiàn)的返回值作為他自己的返回值。

消息的關(guān)鍵在于objc_class,有兩個(gè)字段是我們?cè)诜职l(fā)消息時(shí)關(guān)注的:

-- 指向父類的指針;

-- 一個(gè)類的方法的分發(fā)表,即methodList;

消息的基本框架:

(23)消息轉(zhuǎn)發(fā)

當(dāng)一個(gè)對(duì)象能接收一個(gè)消息時(shí),就會(huì)走正常的方法調(diào)用流程。默認(rèn)情況下,如果是以[object message]的方式調(diào)用方法,如果object無(wú)法響應(yīng)message消息時(shí),編譯器會(huì)報(bào)錯(cuò)。但如果是以perform...的形式來調(diào)用,則需要等到運(yùn)行時(shí)才能確定object是否能接收message消息。如果不能,則程序崩潰。通常,當(dāng)我們不能確定一個(gè)對(duì)象是否能接收某個(gè)消息時(shí),會(huì)先調(diào)用respondsToSelector:來判斷一下:

----------------------------------------------------------------------------------------------------------------------------------------------

當(dāng)一個(gè)對(duì)象無(wú)法接收某一消息時(shí),就會(huì)使用”消息轉(zhuǎn)發(fā)(message forwarding)“機(jī)制。通過這一機(jī)制,我們可以告訴對(duì)象如何處理未知的消息。消息轉(zhuǎn)發(fā)分為三個(gè)步驟:

動(dòng)態(tài)方法解析;

備用接收者;

完整轉(zhuǎn)發(fā);

--- 動(dòng)態(tài)方法解析

對(duì)象在接收到未知的消息時(shí),首先會(huì)調(diào)用所屬類的類方法+resolveInstanceMethod:實(shí)例方法或者+resolveClassMethod:類方法。在這個(gè)方法中,我們有機(jī)會(huì)為該未知消息新增一個(gè)處理方法,不過使用該方法的前提是我們已經(jīng)實(shí)現(xiàn)了該處理方法,只需要在運(yùn)行時(shí)通過class_addMethod函數(shù)動(dòng)態(tài)添加到類里面就行了。如下代碼所示:

[objc]view plaincopyprint?

voidfunctionForMethod1(idself,SEL_cmd)?{

NSLog(@"%@,?%p",self,?_cmd);

}

+?(BOOL)resolveInstanceMethod:(SEL)sel?{

NSString*selectorString?=?NSStringFromSelector(sel);

if([selectorStringisEqualToString:@"method1"])?{

class_addMethod(self.class,@selector(method1),?(IMP)functionForMethod1,"@:");

}

return[superresolveInstanceMethod:sel];

}

---備用接收者

如果上一步無(wú)法處理消息,則Runtime會(huì)繼續(xù)調(diào)用以下方法:

如果一個(gè)對(duì)象實(shí)現(xiàn)了這個(gè)方法,并返回一個(gè)非nil的結(jié)果,則這個(gè)對(duì)象會(huì)作為消息的新接收者,且消息會(huì)被分發(fā)到這個(gè)對(duì)象。當(dāng)然這個(gè)對(duì)象不能是self自身,否則會(huì)出現(xiàn)無(wú)線循環(huán)。

--- 完整消息轉(zhuǎn)發(fā)

如果在上一步還不能處理未知消息,則唯一能做的就是啟用完整的消息轉(zhuǎn)發(fā)機(jī)制了,此時(shí)會(huì)調(diào)用以下方法:

運(yùn)行時(shí)系統(tǒng)會(huì)在這一步給消息接收者最后一次機(jī)會(huì)將消息轉(zhuǎn)發(fā)給其他對(duì)象。對(duì)象會(huì)創(chuàng)建一個(gè)表示消息的NSInvocation對(duì)象,把與尚未處理的消息有關(guān)的全部細(xì)節(jié)都封裝在NSInvocation中,包括selector,target和參數(shù)。我們可以在forwardInvocation方法中將消息轉(zhuǎn)發(fā)給其他對(duì)象。

同時(shí)我們必須重寫以下方法:

END

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,786評(píng)論 6 534
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,656評(píng)論 3 419
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,697評(píng)論 0 379
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,098評(píng)論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,855評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,254評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,322評(píng)論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,473評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,014評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,833評(píng)論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,016評(píng)論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,568評(píng)論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,273評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,680評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,946評(píng)論 1 288
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,730評(píng)論 3 393
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,006評(píng)論 2 374

推薦閱讀更多精彩內(nèi)容

  • 參考鏈接: http://www.cnblogs.com/ioshe/p/5489086.html 簡(jiǎn)介 Runt...
    樂樂的簡(jiǎn)書閱讀 2,148評(píng)論 0 9
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,753評(píng)論 0 9
  • 簡(jiǎn)介 Runtime 又叫運(yùn)行時(shí),是一套底層的 C 語(yǔ)言 API,其為 iOS 內(nèi)部的核心之一,我們平時(shí)編寫的 O...
    專業(yè)男神經(jīng)閱讀 914評(píng)論 0 2
  • 簡(jiǎn)介 Runtime 又叫運(yùn)行時(shí),是一套底層的 C 語(yǔ)言 API,其為 iOS 內(nèi)部的核心之一,我們平時(shí)編寫的 O...
    鄭軍紅閱讀 602評(píng)論 1 2
  • 作為漢族最古老的戲劇—秦腔,在8000年前的天水牧馬灘孕育,定型在唐朝,鼎盛在乾隆年間,用頑強(qiáng)的生命力一直綿延至...
    幼恩wings閱讀 1,647評(píng)論 0 6