最近在研究Runtime,因此,打算寫(xiě)一篇文章跟小伙伴兒們分享一下。好了,廢話(huà)不多說(shuō),直接上干貨。
RunTime簡(jiǎn)稱(chēng)運(yùn)行時(shí)。OC就是運(yùn)行時(shí)機(jī)制,也就是在運(yùn)行時(shí)候的一些機(jī)制,其中最主要的是消息機(jī)制。簡(jiǎn)單說(shuō)一下C與OC在編譯和運(yùn)行階段的區(qū)別,對(duì)于C語(yǔ)言,函數(shù)的調(diào)用在編譯的時(shí)候會(huì)決定調(diào)用哪個(gè)函數(shù)。對(duì)于OC的函數(shù),屬于動(dòng)態(tài)調(diào)用過(guò)程,在編譯的時(shí)候并不能決定真正調(diào)用哪個(gè)函數(shù),只有在真正運(yùn)行的時(shí)候才會(huì)根據(jù)函數(shù)的名稱(chēng)找到對(duì)應(yīng)的函數(shù)來(lái)調(diào)用。
Runtime有5大作用:發(fā)送消息,交換方法,動(dòng)態(tài)添加方法,給分類(lèi)添加屬性,字典轉(zhuǎn)模型,下面一一給大家講解一下這5個(gè)作用。
一、發(fā)送消息
任何方法調(diào)用的本質(zhì)就是發(fā)送一個(gè)消息,用runtime發(fā)送消息,OC底層就是通過(guò)runtime實(shí)現(xiàn)的。下面給大家展示一下底層的代碼:

正常的OC代碼通過(guò)Xcode的編譯器Clang重新編譯,就會(huì)生成底層的代碼,也就是消息機(jī)制的代碼。話(huà)說(shuō)回來(lái),怎么使用編譯器重新編譯呢?我們?cè)诮K端輸入clang -rewrite-objc main.m 就可以得到最終生成代碼了。
我們使用Runtime時(shí),必須要提前導(dǎo)入頭文件<objc/message.h>,可能有人會(huì)問(wèn)我,為什么不導(dǎo)入<objc/runtime.h>?因?yàn)槲覀冞M(jìn)入message.h的聲明中,會(huì)發(fā)現(xiàn)已經(jīng)導(dǎo)入了runtime.h。
上面展示的代碼是最底層的代碼,寫(xiě)著太麻煩了,很少用,下面給大家展示一下我們平常寫(xiě)的代碼:

這個(gè)就是我們平常寫(xiě)的,第一個(gè)參數(shù)的意思是:誰(shuí)發(fā)送消息?????? 第二個(gè)參數(shù)的意思是:發(fā)送什么消息。
其實(shí),還有一種寫(xiě)法,也是可以的:

上面僅僅給大家展示了一些消息機(jī)制底層代碼的一下寫(xiě)法,下面說(shuō)一下Runtime在消息機(jī)制中最重要的一個(gè)作用:“runtime消息機(jī)制,可以調(diào)用私有方法”?。。。。?!
下面給大家展示一下,調(diào)用私有方法:

上面的eat,run方法在Person類(lèi)中均沒(méi)有聲明,只有實(shí)現(xiàn)。
注:我們?cè)谟脤?duì)象p調(diào)用方法時(shí),不要用Person *p = objc_msgSend(object_getClass(@"Person"), sel_registerName("alloc"))這種形式,否則,會(huì)崩。
上面是對(duì)象方法,下面給大家展示一下類(lèi)方法。
對(duì)象方法的對(duì)象調(diào)用,類(lèi)方法的本質(zhì)是類(lèi)對(duì)象調(diào)用。

下面,給大家分享一下方法的調(diào)用流程:
1.去尋找對(duì)應(yīng)的類(lèi)對(duì)象,每一個(gè)對(duì)象都有一個(gè)isa指針,通過(guò)isa指針去對(duì)應(yīng)類(lèi)中查找;
2.注冊(cè)方法編號(hào)
3.根據(jù)方法編號(hào)查找對(duì)應(yīng)的方法
4.找到只是最終函數(shù)實(shí)現(xiàn)地址,根據(jù)地址去方法區(qū)調(diào)用對(duì)應(yīng)函數(shù)。
二、交換方法
交換方法是Runtime中最常用的,我們?cè)谧鲰?xiàng)目時(shí)經(jīng)常用到。
Runtime(交換方法):只要想修改系統(tǒng)的方法實(shí)現(xiàn)。
比如:有一個(gè)項(xiàng)目,已經(jīng)開(kāi)發(fā)了2年,忽然項(xiàng)目負(fù)責(zé)人添加一個(gè)功能,每次UIImage加載圖片,告訴我是否加載成功?
這樣的一個(gè)需求,除了使用Runtime交換方法,用其他的方法很難實(shí)現(xiàn)。
交換方法的步驟為:(1)給系統(tǒng)的方法添加分類(lèi);
??????????????????????????????? (2)自己實(shí)現(xiàn)一個(gè)帶有擴(kuò)展功能的方法;
??????????????????????????????? (3)交換方法的實(shí)現(xiàn),只需要交換一次。
下面直接上代碼:
分類(lèi)的聲明:

分類(lèi)的實(shí)現(xiàn):


三、動(dòng)態(tài)添加方法
動(dòng)態(tài)添加方法:OC是懶加載機(jī)制,只要一個(gè)方法實(shí)現(xiàn)了,就會(huì)馬上添加到方法列表中(不管這個(gè)方法有沒(méi)有用過(guò),都會(huì)添加進(jìn)去)。如果某個(gè)類(lèi)中方法比較多,而且有很多方法不常用,需要給每個(gè)方法都生成映射表,加載類(lèi)到內(nèi)存的時(shí)候就比較耗費(fèi)資源,可以使用動(dòng)態(tài)給該類(lèi)添加方法解決。
下面直接上代碼:
Person類(lèi)的實(shí)現(xiàn)部分:



動(dòng)態(tài)添加方法在做項(xiàng)目時(shí)用得比較少。
4、動(dòng)態(tài)添加屬性
我們給系統(tǒng)的類(lèi)添加屬性的時(shí)候,可以使用runtime動(dòng)態(tài)添加屬性。動(dòng)態(tài)添加屬性的本質(zhì):讓某個(gè)屬性和某個(gè)對(duì)象產(chǎn)生一個(gè)關(guān)聯(lián),并不是直接把這個(gè)值的內(nèi)存空間添加到類(lèi)內(nèi)存空間。
代碼如下:

給系統(tǒng)的類(lèi)添加一個(gè)分類(lèi),聲明部分:

實(shí)現(xiàn)部分:

4、字典轉(zhuǎn)模型
字典轉(zhuǎn)模型有兩種方法:1.KVC? 2.Runtime。第三方框架MJExtension底層就是用Runtime字典轉(zhuǎn)模型的。
KVC的實(shí)現(xiàn)原理是:遍歷字典中所有的key,去模型中查找對(duì)應(yīng)的屬性賦值;Runtime實(shí)現(xiàn)原理剛好與KVC相反:通過(guò)runtime,把一個(gè)模型中所有屬性遍歷出來(lái),根據(jù)屬性去字典里面找。
我們可以創(chuàng)建一個(gè)NSObject分類(lèi),專(zhuān)門(mén)字典轉(zhuǎn)模型,以后所有模型都可以通過(guò)這個(gè)分類(lèi)轉(zhuǎn)
代碼如下:
