人生若只如初見旋奢,何事秋風(fēng)悲畫扇。
依舊是網(wǎng)上很多runtime的資料然痊,依舊是看不懂黄绩,,玷过,這里給大家轉(zhuǎn)化一下runtime爽丹,使它由隱晦難懂變得通俗易懂。
(雖然截圖和語言組織的有些凌亂辛蚊,但是大家還是一點(diǎn)一點(diǎn)的閱讀下去吧粤蝎,可以新建一個(gè)工程,跟著我寫的一步一步的自己走一遍袋马,會(huì)有幫助的初澎。)
關(guān)于runtime理論性的東西可以參考我們同事hah的初識(shí)runtime(我倆的文章名字居然差不多)
本文參照:教你快速上手Runtime。謝謝該文作者崢吖崢老師虑凛。有興趣的也可以去崢老師的博客看看去碑宴。
另外哪里寫的不對(duì),可以給我留言桑谍,我會(huì)及時(shí)改進(jìn)的延柠,謝謝大家。
在這里我們對(duì)runtime進(jìn)行如下的介紹:
1锣披、runtime的簡單介紹
2贞间、runtime的消息發(fā)送
3贿条、用runtime交換方法
4、動(dòng)態(tài)添加方法
5增热、動(dòng)態(tài)添加屬性
6整以、runtime替換KVC,進(jìn)行字典轉(zhuǎn)換
下邊開始我們的通俗易懂的路程:
1峻仇、配置環(huán)境:新建一個(gè)工程公黑,在appdelegate里面將viewcontroller設(shè)置為rootcontroller,方便查看打印信息摄咆。
創(chuàng)建一個(gè)person類帆调,繼承自NSObject,下圖的PersonViewController無用豆同。
然后再看一下目前的工程文件結(jié)構(gòu):
按照網(wǎng)上教程我們開始runtime的各個(gè)方法的實(shí)現(xiàn)番刊。
2、發(fā)送消息影锈。舉例:實(shí)例方法和類方法的調(diào)用原理芹务。
我們建立一個(gè)繼承于NSObject的Person
在.m中分別實(shí)現(xiàn)實(shí)例方法和類方法:
然后在viewcontroller加入頭文件:#import "Person.h"、#import <objc/message.h>
方法的調(diào)用有兩種鸭廷,調(diào)用實(shí)例方法和調(diào)用類方法枣抱。我們用runtime去觀察這兩種方法的實(shí)質(zhì)。
1辆床、viewDidLoad里面實(shí)現(xiàn)Person的實(shí)例化佳晶,并用這個(gè)實(shí)例調(diào)用Person里面的實(shí)例方法;
2讼载、直接用Preson類名或類對(duì)象調(diào)用類方法轿秧。
此時(shí)我們可以看到
但是按照網(wǎng)上教程上圖藍(lán)色框框里面的也應(yīng)該可以放開注釋的,可是不知道為什么我的放開注釋以后會(huì)爆紅:
不知道是為什么(如果大家誰知道怎么回事咨堤,還煩請(qǐng)留言告訴我菇篡,再次謝謝大家了),一喘,驱还,所以這里的runtime并不友好。
哎凸克,終于知道怎么回事了议蟆,還是同事hah幫忙解決的:頭文件導(dǎo)入錯(cuò)了,應(yīng)該是導(dǎo)入:#import <objc/runtime.h>
導(dǎo)入#import<objc/messge.h>也可以調(diào)用objc_msgSend()這個(gè)方法萎战,但是不能傳參咐容,而導(dǎo)入#import<objc/runtime.h>,會(huì)出現(xiàn)這個(gè)方法:objc_msgSend(id, SEL, ...),是可以傳參數(shù)的.
在這里有的網(wǎng)友提到另外一種解決該問題的方案:謝謝北京-吳露撞鹉。方案是:引入頭文件<objc/objc-runtime>疟丙,command+點(diǎn)擊進(jìn)入這個(gè)方法颖侄,我們會(huì)看到下邊的
也就是說這個(gè)頭文件把runtime和message都包含了鸟雏,不過建議還是單獨(dú)引入享郊,因?yàn)檫@兩個(gè)頭文件里面的方法很多都類似,容易混淆孝鹊,區(qū)別在于方法是否可以傳參數(shù)炊琉。就像上邊我們舉例的問題。
打印結(jié)果:
其實(shí)這里咱們可以看到網(wǎng)上教程的注釋:不管是實(shí)例方法還是類方法又活,其本質(zhì)就是讓對(duì)象或者類對(duì)象發(fā)送消息苔咪,即我們?cè)贠C中調(diào)用的實(shí)例方法和類方法在runtime運(yùn)行時(shí)的時(shí)候?qū)嶋H上是轉(zhuǎn)化成C語言的發(fā)送消息的語句。
按照網(wǎng)上教程說的:消息機(jī)制原理:對(duì)象根據(jù)方法編號(hào)SEL去映射表查找對(duì)應(yīng)的方法實(shí)現(xiàn)
其實(shí)說的就是:找到對(duì)應(yīng)的實(shí)例方法或者類方法柳骄。
最后团赏,這里的runtime的使用并不實(shí)用。
3耐薯、交換方法舔清。
其實(shí)寫完下邊的代碼,在通過理解曲初,你會(huì)發(fā)現(xiàn)這中所謂的“交換方法”實(shí)際上就是系統(tǒng)方法的重寫体谒、擴(kuò)展、再替換回去的過程臼婆。
首先我們看一下demo里面viewcontroller的結(jié)構(gòu)(同樣的personviewcontroller無用抒痒,忽略掉,這里的<objc/message.h>最好是換成#import<objc/runtime.h>):
然后我們看viewDidLoad里面颁褂,在這里我們沒有做什么操作故响,只是命名了一個(gè)image:這里我們的圖片是在工程文件中的,所以走到替換方法里面的時(shí)候會(huì)判斷不為空颁独,所以對(duì)應(yīng)的打印信息“加載空的圖片”就不會(huì)打印出來被去。
按照網(wǎng)上教程我們進(jìn)行步驟一的操作,寫一個(gè)名字叫做+ (instancetype)imageWithName:(NSString*)name的類方法奖唯。這個(gè)方法是為了替換的時(shí)候準(zhǔn)備的惨缆。
然后我們進(jìn)行步驟二,這個(gè)步驟二很關(guān)鍵丰捷,就是替換的核心所在坯墨。
注意:兩個(gè)方法。前面一個(gè)是準(zhǔn)備好的方法病往,后邊一個(gè)是系統(tǒng)的方法捣染,要用第一個(gè)方法換掉第二個(gè)方法,這里需要注意替換順序停巷。
當(dāng)我們將viewDidLoad里面的image換成一個(gè)空的圖片的時(shí)候
我們看到打印信息
是走了圖片為空的if判斷里面的耍攘。
所以榕栏,這個(gè)方法可以看成是網(wǎng)絡(luò)請(qǐng)求一張圖片,如果請(qǐng)求失敗蕾各,可以用默認(rèn)的圖片進(jìn)行展示扒磁,就像下邊的方法,網(wǎng)絡(luò)請(qǐng)求失敗的時(shí)候會(huì)展示一張默認(rèn)圖式曲。
當(dāng)然妨托,這里不是說runtime這么高大上的方法僅僅能實(shí)現(xiàn)這么low的功能,僅做示例而已吝羞。
其實(shí)這個(gè)方法替換可以用在線下替換線上代碼里面兰伤,“黑魔法”,我還不會(huì)钧排,只是聽說過敦腔。下邊繼續(xù)學(xué)習(xí)runtime其他的方法。
4恨溜、動(dòng)態(tài)添加方法符衔。
動(dòng)態(tài)添加方法,咱們的person類.h里面用到了分類:
在這里拓展一下類擴(kuò)展的知識(shí):類擴(kuò)展與分類的區(qū)別筒捺。謝謝作者:Mitchell孟晨
在這里大家是不是會(huì)想到我們的面試題中:
有的還是用英語問的:
類的作用柏腻。參考自category解析。如果大家對(duì)類別特別感興趣可以參考深入解析OC中的Category
當(dāng)我們?cè)陬愔刑砑覢interface的時(shí)候:
會(huì)出現(xiàn)上圖中的3個(gè)系吭。
第一個(gè)就是類:繼承自誰五嫂。
第二個(gè)是分類,就是分類肯尺。
類擴(kuò)展與分類的區(qū)別中有舉例說明:雖然我們?cè)诜诸愔新暶鲗傩圆粫?huì)報(bào)錯(cuò)沃缘,但是@property并沒有自動(dòng)為我們?cè)O(shè)置的屬性生成set、get方法则吟。
第三個(gè)就是extension槐臀。類擴(kuò)展。
再看上述中有一句話是這樣說的:
類擴(kuò)展的作用:
person類.m中:
viewcontroller里面:
這里沒有實(shí)現(xiàn)eat4方法氓仲,所以會(huì)在viewcontroller中的viewdidload里面調(diào)用eat4的時(shí)候水慨,會(huì)調(diào)用這個(gè)方法處理,并且把對(duì)應(yīng)的方法列表傳過來敬扛。用來判斷為實(shí)現(xiàn)的方法是不是我們想要?jiǎng)討B(tài)添加的方法晰洒。
這里注意的是,崢老師教程里面在調(diào)用class_addMethod(self,@selector(eat4), (IMP)eatt,"v@:");這個(gè)方法的時(shí)候啥箭,第三個(gè)參數(shù)前面并沒有添加“(IMP)”谍珊,不添加這個(gè)IMP,會(huì)報(bào)黃:
說的什么不清楚急侥,意思就是叫你添加上IMP砌滞。
拓展一下performSelector調(diào)用和直接調(diào)用的區(qū)別:
準(zhǔn)備好了侮邀,我們看打印結(jié)果:
這里我們可以看到NSStringFromSelector(sel)調(diào)用的就是eat4。
扯了這么多怎么用贝润,到底這個(gè)動(dòng)態(tài)添加方法到底是用來干什么的呢绊茧,看看眾多網(wǎng)友的理解。翻了好多题暖,就下邊的兩個(gè)解釋比較靠譜按傅,大家湊合著看吧:
當(dāng)然還有最一開始咱們引入動(dòng)態(tài)添加的時(shí)候的目的:
不忘初心捉超,方得始終胧卤。
5、動(dòng)態(tài)添加屬性拼岳。
想知道原理的請(qǐng)移步OC Associated Objects實(shí)現(xiàn)原理
原理:給一個(gè)類聲明屬性枝誊,其實(shí)本質(zhì)就是給這個(gè)類添加關(guān)聯(lián),并不是直接把這個(gè)值的內(nèi)存空間添加到類存空間惜纸。
看結(jié)構(gòu)叶撒,添加了一個(gè)NSObject的分類:
分類的.h文件,將set方法和name外漏耐版,方便在viewcontroller里面調(diào)用祠够。
分類的.m文件,set方法和name的實(shí)現(xiàn)粪牲。
最后看viewcontroller里面古瓤,引入分類的頭文件,然后實(shí)例化NSObject腺阳,調(diào)用objc1的name落君。
最后的打印結(jié)果:
tips:其實(shí)呢這個(gè)動(dòng)態(tài)添加屬性,是在哪里用到的呢亭引?具體的我也說不上來绎速。其實(shí)按照我的理解,咱們這里所謂的動(dòng)態(tài)添加屬性焙蚓,是在分類上邊添加的屬性纹冤,那么我就想了,為什么我不能在類自己本身上邊添加屬性呢购公?對(duì)吧萌京?為什么非要在分類上邊添加呢,還整了個(gè)動(dòng)態(tài)添加屬性君丁,麻煩不枫夺?!原因有兩個(gè):
1)如果你的類自己本身里面的邏輯或者代碼比較多绘闷,而需求是新加一個(gè)功能橡庞,那么就可以在分類上邊添加较坛,因?yàn)榉诸惡屠^承還是有區(qū)別的。咱們這樣理解鞍亲睢:如果你用的是繼承丑勤,那么在子類a里面新添加的屬性和方法,在父類以及其他繼承父類的子類b里面是不能用新添加的屬性和方法的吧趣,(有點(diǎn)繞)法竞。但是分類就行,你在分類里面新添加了屬性和方法强挫,那么在其他用到這個(gè)類自己的地方岔霸,是可以用新添加的屬性和方法的。好像和公有性俯渤、私有性有點(diǎn)關(guān)系呆细。
2)給系統(tǒng)的類添加屬性。像UIView八匠、UIImageView絮爷、UILabel、UIButton等等系統(tǒng)的類梨树,你覺得系統(tǒng)的類里面的屬性不夠用(當(dāng)然坑夯,像我這種小白還是覺得夠用的,關(guān)鍵不知道各個(gè)類里面除了經(jīng)常用的還有啥抡四,柜蜈,,)就可以自己添加屬性床嫌,當(dāng)然了跨释,在MJ和SDWebimage里面其實(shí)也能看到動(dòng)態(tài)添加屬性的影子,只不過是我們大多數(shù)人沒有進(jìn)入人家里面去看具體的代碼厌处。同事hah在runtime初識(shí)里面也提到了這個(gè):
6鳖谈、字典轉(zhuǎn)模型
崢老師說的“字典轉(zhuǎn)模型的方式一:KVC”這個(gè)就是咱們平時(shí)JSON解析的時(shí)候經(jīng)常用到的,這里不作進(jìn)一步解釋阔涉,看看崢老師說的用runtime字典轉(zhuǎn)模型的方式缆娃。
沒搞明白。瑰排。贯要。
先用KVC吧。
總的來說椭住,即便咱們知道了runtime是怎么用的崇渗,但是沒有合適的環(huán)境使用runtime,也是白搭。所以宅广,咱們現(xiàn)在能做的就是先了解runtime葫掉,隨時(shí)記在心中,以后哪里能夠用到的跟狱,可以嘗試著用runtime的特性和上述幾個(gè)方法來實(shí)現(xiàn)俭厚,甚至于用runtime解決bug。
另外這里有很多關(guān)于runtime的集合驶臊,大家可以參考:runtime專題