ios運(yùn)行時(shí)

前言

什么是運(yùn)行時(shí)(runtime)?

首先我們要先知道編程語(yǔ)言有靜態(tài)和動(dòng)態(tài)之分生百。所謂靜態(tài)語(yǔ)言递雀,就是在程序運(yùn)行前決定了所有的類(lèi)型判斷,類(lèi)的所有成員蚀浆、方法在編譯階段就確定好了內(nèi)存地址缀程。也就意味著所有類(lèi)對(duì)象只能訪(fǎng)問(wèn)屬于自己的成員變量和方法,否則編譯器直接報(bào)錯(cuò)市俊。比較常見(jiàn)的靜態(tài)的語(yǔ)言如:java杨凑,c++,c等等摆昧。

而動(dòng)態(tài)語(yǔ)言撩满,恰恰相反,類(lèi)型的判斷绅你、類(lèi)的成員變量鹦牛、方法的內(nèi)存地址都是在程序的運(yùn)行階段才最終確定,并且還能動(dòng)態(tài)的添加成員變量和方法勇吊。也就意味著你調(diào)用一個(gè)不存在的方法時(shí)曼追,編譯也能通過(guò),甚至一個(gè)對(duì)象它是什么類(lèi)型并不是表面我們所看到的那樣汉规,只有運(yùn)行之后才能決定其真正的類(lèi)型礼殊。相比于靜態(tài)語(yǔ)言,動(dòng)態(tài)語(yǔ)言具有較高的靈活性和可訂閱性针史。而oc晶伦,正是一門(mén)動(dòng)態(tài)語(yǔ)言。

介紹到這里啄枕,我想可以解釋一下運(yùn)行時(shí)是什么了婚陪?所謂運(yùn)行時(shí),就是程序在運(yùn)行時(shí)做的一些事频祝。蘋(píng)果提供了一套純c語(yǔ)言的api泌参,即runtime。在iOS開(kāi)發(fā)中runtime的特性使得oc這門(mén)語(yǔ)言具有獨(dú)特的魅力,我們可以利用運(yùn)行時(shí)處理一些特殊的事情常空,甚至你可以輕松的玩出一些逼格很高的花樣來(lái)沽一。下面就開(kāi)始一起進(jìn)入運(yùn)行時(shí)的世界吧。

在正式進(jìn)入篇幅之前漓糙,首先聲明一下铣缠,本編的主旨是簡(jiǎn)要闡述運(yùn)行時(shí)的一些機(jī)制和原理,重點(diǎn)是講述運(yùn)行時(shí)的一些常用用法,不會(huì)去深入探究底層的C語(yǔ)言api蝗蛙。

要了解運(yùn)行時(shí)蝇庭,我們得先了解oc的消息機(jī)制

那么什么是消息機(jī)制?

在Objective-C中捡硅,任何方法的調(diào)用遗契,本質(zhì)是發(fā)送消息。比如我們下面方法:

[obj ?method];

編譯器會(huì)自動(dòng)轉(zhuǎn)化為:

objc_msgSend(obj, @selector (method));

也就是說(shuō)我們?cè)趏c中調(diào)用任何一個(gè)方法病曾,其實(shí)質(zhì)是轉(zhuǎn)換為runtime中的一個(gè)函數(shù)objc_msgSend()牍蜂,這個(gè)函數(shù)的作用是向obj對(duì)象(方法的調(diào)用者)發(fā)送了一條消息,告訴它你該去執(zhí)行某個(gè)方法泰涂。

所以鲫竞,我們其實(shí)也可以直接用運(yùn)行時(shí)去調(diào)用你想要調(diào)用的任何一個(gè)可調(diào)用的方法:

如:

Dog? *dog = [Dog alloc] init];

[dog run:100];

等價(jià)于:

Dog? *dog? = objc_msgSend(objc_getClass("Dog"), @selector(alloc));

dog = objc_msgSend(dog, sel_registerName("init"));

objc_msgSend(dog, sel_registerName("run:"),100); //調(diào)用帶參數(shù)的方法

注:使用objc_msgSend()函數(shù)逼蒙,須要先import

講到這里从绘,我們就可以說(shuō)一說(shuō)什么是oc消息機(jī)制,也就是一個(gè)方法的調(diào)用流程是牢。

1僵井、編譯器會(huì)先將代碼[obj? method]轉(zhuǎn)化為objc_msgSend(obj, @selector (method))函數(shù)去執(zhí)行。

2驳棱、在objc_msgSend()函數(shù)中,首先通過(guò)obj的isa指針找到(對(duì)象)obj對(duì)應(yīng)的(類(lèi))class批什。

3、在class中會(huì)先去cache中 通過(guò)SEL查找對(duì)應(yīng)函數(shù)method(cache中method列表是以SEL為key通過(guò)hash表來(lái)存儲(chǔ)的社搅,這樣能提高函數(shù)查找速度)驻债,若 cache中未找到。再去class中的消息列表methodList中查找形葬,若methodlist中未找到合呐,則取superClass中查找。若能找到笙以,則將method加 入到cache中淌实,以方便下次查找,并通過(guò)method中的函數(shù)指針跳轉(zhuǎn)到對(duì)應(yīng)的函數(shù)中去執(zhí)行猖腕。

補(bǔ)充:

>在oc中拆祈,每一個(gè)對(duì)象都有一個(gè)isa指針變量,這個(gè)指針指向的是對(duì)象的類(lèi)谈息,我們可以通過(guò)isa指針訪(fǎng)問(wèn)一個(gè)對(duì)象的類(lèi)

>方法都保存在類(lèi)的消息列表中缘屹,這個(gè)列表其實(shí)是一個(gè)字典,key是selector侠仇,value是IMP(imp是一個(gè)指針類(lèi)型,指向方法的實(shí)現(xiàn)),并且selector和IMP之間的關(guān)系是在運(yùn)行時(shí)才決定的逻炊,而不是編譯時(shí)互亮。如此們就可以做出一些特別事情來(lái)。

我們可以用運(yùn)行時(shí)做什么

1余素、互換方法的實(shí)現(xiàn)

上面說(shuō)到selector和IMP之間的關(guān)系是在運(yùn)行時(shí)才決定的豹休,那我們是不是可以改變selector和IMP的對(duì)應(yīng)關(guān)系呢?runtime就給我們提供了這么一個(gè)函數(shù):

void method_exchangeImplementations(Method m1, Method m2)

我們可以通過(guò)此函數(shù)交換兩個(gè)方法的實(shí)現(xiàn)桨吊,在開(kāi)發(fā)中威根,可能我們會(huì)經(jīng)常遇到一種場(chǎng)景,想為系統(tǒng)的某個(gè)方法增加一些特定的功能视乐,又不想改變?cè)械臇|西洛搀,想要做到無(wú)縫銜接,用runtime方法互換無(wú)疑是最完美的佑淀。下面以交換系統(tǒng)的dealloc方法的實(shí)現(xiàn)為例:

首先建一個(gè)NSObject類(lèi)目NSObject+ExchangeMethod留美,在類(lèi)目中為NSObject類(lèi)擴(kuò)展一個(gè)my_dealloc方法用于替換系統(tǒng)的dealloc方法,其.m文件實(shí)現(xiàn)如下:

這樣伸刃,當(dāng)一個(gè)類(lèi)的dealloc方法被調(diào)用時(shí)谎砾,會(huì)執(zhí)行my_dealloc方法里的實(shí)現(xiàn),完全無(wú)需再對(duì)原有的代碼做任何改動(dòng)

2捧颅、動(dòng)態(tài)添加方法

前面有說(shuō)到景图,動(dòng)態(tài)語(yǔ)言調(diào)用一個(gè)沒(méi)有的方法時(shí),編譯階段也不不會(huì)報(bào)錯(cuò)碉哑。比如:

Dog *dog = [Dog alloc] init];

[dog performSelector:@selector(eat)]];

注:dog類(lèi)中沒(méi)有聲明也沒(méi)實(shí)現(xiàn)eat方法

上面代碼症歇,編譯階段肯定會(huì)通過(guò),但程序一運(yùn)行時(shí)便直接拋出異常閃退谭梗,拋出異常的打印閉著眼睛也知道是 :-[Dog eat]: unrecognized selector sent to instance 0x7fac91d0eba0'忘晤。這也印證了動(dòng)態(tài)語(yǔ)言的方法需要在運(yùn)行階段才最終確定。

從而激捏,我們可以動(dòng)態(tài)的為某個(gè)類(lèi)添加方法设塔,而蘋(píng)果performSelector:這個(gè)方法也很好的為我們逃過(guò)編譯報(bào)錯(cuò)提供了支持。示例代碼如下:

屆時(shí)远舅,我們?cè)谌缟厦嬲{(diào)用[dog performSelector:@selector(eat)]]時(shí)闰蛔,就會(huì)去執(zhí)行test函數(shù)了。

3图柏、動(dòng)態(tài)添加屬性

這也是runtime的一個(gè)重量級(jí)功能了序六,我們經(jīng)常會(huì)想為系統(tǒng)的類(lèi)或者一些不便修改的第三方框架的類(lèi)增加一些自定義的屬性以滿(mǎn)足開(kāi)發(fā)的需求。這個(gè)時(shí)候我們還是首先會(huì)想到類(lèi)目蚤吹,但是問(wèn)題來(lái)了例诀,類(lèi)目只能為一個(gè)類(lèi)添加方法随抠,不能添加屬性。

怎么做呢繁涂,還是用到運(yùn)行時(shí)拱她,為類(lèi)動(dòng)態(tài)添加屬性。示例代碼:為NSobject類(lèi)添加一個(gè)字符串類(lèi)型的屬性: NSString *name

首先我們還是為NSobject建一個(gè)類(lèi)目扔罪,其.h文件如下:

這里我們用property秉沼,類(lèi)目中用 property會(huì)自動(dòng)生成set/get的聲明,但是沒(méi)有實(shí)現(xiàn)矿酵,也無(wú)法生成下劃線(xiàn)的成員變量唬复,我們需要手動(dòng)實(shí)現(xiàn)set、get方法全肮。其.m文件如下:

4敞咧、獲取類(lèi)中所有的成員變量和屬性

在開(kāi)發(fā)中,你可能會(huì)遇到想要改變系統(tǒng)自帶的類(lèi)的某一個(gè)值倔矾,卻找不與之對(duì)應(yīng)的api妄均,然后你就在那找瞎了眼,找呀找哪自,始終找不到丰包。這個(gè)時(shí)候我們可以確定一點(diǎn),蘋(píng)果系統(tǒng)自帶的類(lèi)有很多私有的屬性或成員變量沒(méi)有公開(kāi)出來(lái)壤巷,也就意味著蘋(píng)果它不想讓我們?cè)L問(wèn)邑彪。我靠,那還搞毛胧华,有時(shí)需求來(lái)了寄症,我還非要訪(fǎng)問(wèn)不可,那怎么辦矩动?

用運(yùn)行時(shí)獲取類(lèi)的所有成員變量有巧,即便私有的也能獲取的到,用的函數(shù)如下:

Ivar *class_copyIvarList(Class cls, unsigned int *outCount)//獲取類(lèi)中所有的成員變量

objc_property_t class_getProperty(Class cls, const char *name)//獲取類(lèi)中所有的屬性

代碼示例如下:

使用運(yùn)行時(shí)獲取類(lèi)中所有成員變量悲没,還是相當(dāng)有用的篮迎,比如現(xiàn)在一些字典轉(zhuǎn)模型框架,它需要獲取到模型的所有屬性名示姿,以這個(gè)屬性名為key甜橱,取到字典中對(duì)應(yīng)的value,然后通過(guò)kvc給這個(gè)屬性賦設(shè)置值栈戳。再比如岂傲,你要設(shè)置系統(tǒng)UITextField控件placeholder的顏色,你會(huì)發(fā)現(xiàn)你翻遍api也找不到一個(gè)屬性和方法來(lái)設(shè)置子檀,這時(shí)你用運(yùn)行時(shí)獲取UITextField類(lèi)所有成員變量镊掖,你會(huì)發(fā)現(xiàn)有一個(gè)_placeholderLabel成員變量乃戈,我們只需:

[self.textField setValue:[UIColor blueColor] forKeyPath:@"_placeholderLabel.textColor"];

總結(jié)

以上便是運(yùn)行時(shí)的一些常用用法,本文僅拋磚引玉堰乔,在ios開(kāi)發(fā)的道路上偏化,想要深入了解oc這門(mén)語(yǔ)言脐恩,runtime是一餐不容錯(cuò)過(guò)的盛宴镐侯。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市驶冒,隨后出現(xiàn)的幾起案子苟翻,更是在濱河造成了極大的恐慌,老刑警劉巖骗污,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件崇猫,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡需忿,警方通過(guò)查閱死者的電腦和手機(jī)诅炉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)屋厘,“玉大人涕烧,你說(shuō)我怎么就攤上這事『谷鳎” “怎么了议纯?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)溢谤。 經(jīng)常有香客問(wèn)我瞻凤,道長(zhǎng),這世上最難降的妖魔是什么世杀? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任阀参,我火速辦了婚禮,結(jié)果婚禮上瞻坝,老公的妹妹穿的比我還像新娘蛛壳。我一直安慰自己,他們只是感情好湿镀,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布炕吸。 她就那樣靜靜地躺著,像睡著了一般勉痴。 火紅的嫁衣襯著肌膚如雪赫模。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 50,096評(píng)論 1 291
  • 那天蒸矛,我揣著相機(jī)與錄音瀑罗,去河邊找鬼胸嘴。 笑死,一個(gè)胖子當(dāng)著我的面吹牛斩祭,可吹牛的內(nèi)容都是我干的劣像。 我是一名探鬼主播,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼摧玫,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼耳奕!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起诬像,我...
    開(kāi)封第一講書(shū)人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤屋群,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后坏挠,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體芍躏,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年降狠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了对竣。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡榜配,死狀恐怖否纬,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情芥牌,我是刑警寧澤烦味,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布,位于F島的核電站壁拉,受9級(jí)特大地震影響谬俄,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜弃理,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一溃论、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧痘昌,春花似錦钥勋、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至驻啤,卻和暖如春菲驴,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背骑冗。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工赊瞬, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留先煎,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓巧涧,卻偏偏與公主長(zhǎng)得像薯蝎,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子谤绳,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351

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