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ì)象只能訪問(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 <objc/message.h>


講到這里,我們就可以說(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指針訪問(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)增加一些自定義的屬性以滿足開(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ú)法生成下劃線的成員變量,我們需要手動(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)了,我還非要訪問(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)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,041評(píng)論 0 9
  • 本文轉(zhuǎn)載自:http://yulingtianxia.com/blog/2014/11/05/objective-...
    ant_flex閱讀 878評(píng)論 0 1
  • 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的...
    西木閱讀 30,881評(píng)論 33 466
  • 今天第四天了,今天課程講了三位一體的標(biāo)簽話管理,從時(shí)間、空間、環(huán)境、特征來(lái)做標(biāo)識(shí)、以及三種蘋(píng)果的運(yùn)用! 這次被選中...
    芳沁雅閱讀 279評(píng)論 0 0
  • 自我提升閱讀學(xué)習(xí)第153天(9月17日) 緊張的三天培訓(xùn)終于拿到了冠軍,帶著激動(dòng)地心情,我們登上了回家的高...
    vv167閱讀 465評(píng)論 0 1

友情鏈接更多精彩內(nèi)容