Object-C Runtime 運(yùn)行時(shí)

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

參考文章:
玉令天下的博客
特酷吧
南峰子的技術(shù)博客
顧 鵬


Objective-C是動(dòng)態(tài)語言拦焚,所以需要編譯器的同時(shí)也需要一個(gè)運(yùn)行時(shí)系統(tǒng)來動(dòng)態(tài)創(chuàng)建對(duì)象夸楣、傳遞消息碴里。在你需要的時(shí)候還能對(duì)其進(jìn)行擴(kuò)展,解決問題谈火。Objective-C的Runtime是一個(gè)運(yùn)行時(shí)的庫(Runtime Library),主要使用C和匯編寫的庫,為C添加了面向?qū)ο蟮哪芰Σ?chuàng)造了Objective-C筒占。這個(gè)運(yùn)行時(shí)系統(tǒng)就像一個(gè)操作系統(tǒng)一樣:它讓所有的工作正常運(yùn)行。有動(dòng)態(tài)類型(Dynamic typing),動(dòng)態(tài)綁定(Dynamic binding)和動(dòng)態(tài)加載(Dynamic loading)
Runtime有兩個(gè)版本:ModernLegacy团赁。Object—C2.0采用的是Modern版本的Runtime育拨,只能在iOS和OS X10.5之后的64位程序上運(yùn)行。32位程序采用Legacy版本的Runtime欢摄。它們的區(qū)別在于更改實(shí)例變量時(shí),Legary需要重新編譯其子類熬丧,而Modern`則不需要。

  • 封裝怀挠,在這個(gè)庫中锹引,對(duì)象可以用C語言中的結(jié)構(gòu)體表示,而方法可以用C函數(shù)來實(shí)現(xiàn)唆香,這些結(jié)構(gòu)體和函數(shù)被runtime封裝后嫌变,可以創(chuàng)建檢查修改類對(duì)象和他們的方法。

  • 找出方法的最終執(zhí)行代碼:當(dāng)程序執(zhí)行object doSomething時(shí)躬它,會(huì)向消息接受者發(fā)送一條消息腾啥,runtime會(huì)根據(jù)消息接受者是否能響應(yīng)該消息而做出不同的反應(yīng)。

  • 在編譯時(shí)你寫的Objective-C 函數(shù)調(diào)用的語法都會(huì)被翻譯成一個(gè)C的函數(shù)調(diào)用,例如我們發(fā)送一個(gè)消息[receiver message]會(huì)被編譯器轉(zhuǎn)化為[objc_msgSend(receiver,selector)]如果有參數(shù)的話則為[objc_msgSend(receiver,selector,arg1,arg2)],如果消息的接受者能夠找到對(duì)應(yīng)的方法,就執(zhí)行此方法,如果沒有,要么消息被轉(zhuǎn)發(fā),或是動(dòng)態(tài)添加此消息,以上都沒有的話程序就會(huì)crash冯吓。

  • 從Object-C是動(dòng)態(tài)語言理解的話就是,[receiver message]在編譯的時(shí)候只是知道我將要向receiver發(fā)送一個(gè)message但是還沒發(fā)送甚至不知道是否有這個(gè)消息以及這個(gè)消息有沒有具體實(shí)現(xiàn),只有在程序 運(yùn)行時(shí)才回去查找實(shí)現(xiàn),所以說Runtime對(duì)于Object-C來說非常重要,但其實(shí)通過終端我們是可以看到轉(zhuǎn)換后的樣子的倘待。

打開終端進(jìn)入工程目錄

找到工程的.main文件層級(jí) 輸入命令 clang -rewrite-objc main.m
文件夾內(nèi)就可以找到.cpp文件,打開可以看到很多C的代碼所以有人說OC是假的面向?qū)ο?/p>

  • isMemberOfClass 只判斷是否屬于當(dāng)前類的 實(shí)例 至于父類是什么關(guān)系它不管
  • isKindOfClass 則能判斷繼承關(guān)系
  • respondsToSelector 判斷實(shí)例是否有這樣方法
  • instancesRespondToSelector 判斷類是否有這個(gè)方法不能用在類的對(duì)象

Class為指向類的結(jié)構(gòu)體指針typedef struct objc_class *Class;组贺,該類的結(jié)構(gòu)體包含:

struct objc_class {

    Class isa  OBJC_ISA_AVAILABILITY; // isa指針


#if !__OBJC2__

    Class super_class                  OBJC2_UNAVAILABLE;  // 父類

    const char *name                   OBJC2_UNAVAILABLE;  // 類名

    long version                       OBJC2_UNAVAILABLE;  // 類的版本信息凸舵,默認(rèn)為0

    long info                          OBJC2_UNAVAILABLE;  // 類信息,供運(yùn)行期使用的一些位標(biāo)識(shí)

    long instance_size                 OBJC2_UNAVAILABLE;  // 該類的實(shí)例變量大小

    struct objc_ivar_list *ivars       OBJC2_UNAVAILABLE;  // 該類的成員變量鏈表

    struct objc_method_list **methodLists   OBJC2_UNAVAILABLE;  // 方法定義的鏈表

    struct objc_cache *cache           OBJC2_UNAVAILABLE;  // 方法緩存

    struct objc_protocol_list *protocols    OBJC2_UNAVAILABLE;  // 協(xié)議鏈表

#endif


} OBJC2_UNAVAILABLE;

isa 指針指向objc_class結(jié)構(gòu)體的指針

  • 每一個(gè)類的實(shí)例對(duì)象都有一個(gè)isa指針指向它的類失尖,而每個(gè)類也有個(gè)isa指針指向它的元類 [metaClass] 元類里保存了類方法的列表啊奄。
  NSObject *objc = [[NSObject alloc]init];
  objc->isa; // 警告:會(huì)提示你用 **object_getClass(objc);**替換
  • 當(dāng)實(shí)例對(duì)象調(diào)用實(shí)例方法時(shí),isa指針會(huì)到該實(shí)例方法的類Subclass(class)中去查詢是否有此實(shí)例方法掀潮,如果沒有則會(huì)去它的父類Superclass(class)中去查找菇夸,一層一層向上查找。
  • 當(dāng)調(diào)用類方法時(shí)仪吧,isa指針會(huì)去該類元類中去查找Subclass(meta)如果沒有則會(huì)去到元類的父類中去查找 Superclass(meta)
  • 萬物皆對(duì)象庄新,元類也不例外,元類也有它的isa指針薯鼠,元類的isa指針只指向元類的根 根元類Root Class(meta) 同理根源類也是對(duì)象择诈,它的isa指針指向 自己本身 因?yàn)楦愐呀?jīng)是最頂層的根類就像NSObject或NSProxy,它的父類則指向nil出皇。
    上面所述的三條總結(jié)起來就如下圖:
SunnyXX之前的題

為什么[NSObject foo]可以調(diào)用實(shí)例方法羞芍?

Method 方法的類型

typedef struct objc_method *Method;
objc_method結(jié)構(gòu)體如下

struct objc_method {
    SEL method_name                                          OBJC2_UNAVAILABLE;
    char *method_types                                       OBJC2_UNAVAILABLE;
    IMP method_imp                                           OBJC2_UNAVAILABLE;
}  
  • method_name很顯然是方法名 類型是SEL稍后解釋。
  • method_types char類型的指針用來存儲(chǔ)參數(shù)類型和返回值類型恶迈。
  • method_imp IMP類型 是個(gè)函數(shù)指針 稍后解釋涩金。

SEL

等同于Object-C中selectorselector是方法選擇器
typedef struct objc_selector *SEL;SEL是指向objc_selector的指針暇仲,不同類中相同名字的方法所對(duì)應(yīng)的方法選擇器是相同的步做,即使方法名字相同而變量類型不同也會(huì)導(dǎo)致它們具有相同的方法選擇器 因?yàn)椴煌惖膶?shí)例對(duì)象用相同的方法選擇器時(shí),會(huì)在各自的消息選擇奈附、實(shí)現(xiàn)地址全度、方法鏈表中根據(jù) selector 去查找具體的方法實(shí)現(xiàn)IMP。這也是動(dòng)態(tài)的過程斥滤,也就是說在運(yùn)行之前甚至編譯的時(shí)候我們都不知道最終會(huì)執(zhí)行哪行代碼将鸵。

IMP

IMP在objc中的定義為`typedef id (*IMP)(id, SEL, ...);
前面也提到它是個(gè) 函數(shù)指針上面SEL里提到只有在編譯的時(shí)候才會(huì)知道最終實(shí)現(xiàn)哪行代碼,就是因?yàn)榫幾g時(shí)IMP這個(gè)函數(shù)指針指向了最終實(shí)現(xiàn)的代碼佑颇。方法名相同的時(shí)候通過IMP的參數(shù)類型就能將它們區(qū)分開找到特定的那個(gè)方法的實(shí)現(xiàn)顶掉。

Cache

  • [receiver message]當(dāng)對(duì)象接收message時(shí)isa指針會(huì)去對(duì)應(yīng)的對(duì)象中去查找該message,就相當(dāng)于遍歷MethodList挑胸,但是只有message這一個(gè)方法是我們所需要的痒筒,每次接收message時(shí)就要遍歷一遍MethodList,這樣太低效。所以引入了cache,將我們使用過的方法放到緩存中下次調(diào)用時(shí)先去cache中查找如果沒有再去MethodList中遍歷茬贵。

  • objc_cache結(jié)構(gòu)體指針:

struct objc_cache {

    unsigned int mask /* total = mask + 1 */                 OBJC2_UNAVAILABLE;

    unsigned int occupied                                    OBJC2_UNAVAILABLE;

    Method buckets[1]                                        OBJC2_UNAVAILABLE;

};

南峰子的技術(shù)博客對(duì)objc_cache結(jié)構(gòu)體描述的很詳細(xì):

  • mask:一個(gè)整數(shù)簿透,指定分配的緩存bucket的總數(shù)。在方法查找過程中解藻,Objective-C runtime使用這個(gè)字段來確定開始線性查找數(shù)組的索引位置老充。指向方法selector的指針與該字段做一個(gè)AND位操作(index = (mask & selector))。這可以作為一個(gè)簡單的hash散列算法螟左。
  • occupied:一個(gè)整數(shù)啡浊,指定實(shí)際占用的緩存bucket的總數(shù)。
  • buckets:指向Method數(shù)據(jù)結(jié)構(gòu)指針的數(shù)組胶背。這個(gè)數(shù)組可能包含不超過mask+1個(gè)元素虫啥。需要注意的是,指針可能是NULL奄妨,表示這個(gè)緩存bucket沒有被占用涂籽,另外被占用的bucket可能是不連續(xù)的。這個(gè)數(shù)組可能會(huì)隨著時(shí)間而增長

Ivar 實(shí)例變量的類型

typedef struct objc_ivar *Ivar;

struct objc_ivar {
  char *ivar_name                                          OBJC2_UNAVAILABLE; // 名稱
  char *ivar_type                                          OBJC2_UNAVAILABLE; // 類型
  int ivar_offset                                          OBJC2_UNAVAILABLE; // 偏移量
#ifdef __LP64__
  int space                                                OBJC2_UNAVAILABLE;
#endif
}  

id是一個(gè)objc_object類型指針

  • typedef struct objc_object *id;

引入頭文件
#import <objc/runtime.h>
#import <objc/message.h>

`#import <objc/runtime.h>`
`#import <objc/message.h>`
Class cls = [per class];// 獲取自己的類
const char *className =  class_getName(cls); // 獲取類名
NSLog(@"%s",className);

 // 2. meta-class 元類
 // 1>當(dāng)我們調(diào)用實(shí)例方法時(shí)砸抛,系統(tǒng)會(huì)去類的列表里找對(duì)應(yīng)名字的方法
 // 2>當(dāng)我們調(diào)用類方法時(shí)评雌,系統(tǒng)會(huì)去meta-class(元類)的列表中找對(duì)應(yīng)名字的方法
        
  // 尋找對(duì)象的元類 object_getClass(填入的是類對(duì)象)
       Class metaClass =  object_getClass(cls);
       BOOL isMetaClass = class_isMetaClass(metaClass);// 判斷是不是元類
       NSLog(@"是否是元類%d",isMetaClass);
        
       // 3. 獲取屬性列表 如果有屬性count就有值
        unsigned int count = 0;
        Ivar *ivarList = class_copyIvarList(cls,&count);
        // 循環(huán)遍歷類獲取其屬性
        for (int i = 0; i < count; i++) {
            Ivar ivar = ivarList[i];
            NSLog(@"Person類的成員變量列表下標(biāo)%d以及屬性名%s",i,ivar_getName(ivar)); // _name _sex _age _hobby
        }
        
        // 4.獲取屬性列表
        unsigned int outCount = 0;
        objc_property_t *properList = class_copyPropertyList(cls, &outCount);
        for (int i = 0; i < outCount; i++) {
            objc_property_t property = properList[i];
            
            unsigned int attributeCount = 0;
         objc_property_attribute_t *attribute =  property_copyAttributeList(property,&attributeCount); // 屬性列表內(nèi)部
            for (int j = 0 ; j < attributeCount; j++) {
                NSLog(@"%s : %s",attribute[j].name,attribute[j].value);
            }
            
            NSLog(@"Person類的屬性列表下標(biāo)%d以及屬性名%s",i,property_getName(property));
        }
        
        // 方法列表
        unsigned int methCount= 0;
       Method *methList = class_copyMethodList(cls,&methCount);
        for (int i = 0; i < methCount; i++) {
           Method meth = methList[i];
            NSLog(@"Person方法名%@",NSStringFromSelector(method_getName(meth)));
        }

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市直焙,隨后出現(xiàn)的幾起案子景东,更是在濱河造成了極大的恐慌,老刑警劉巖奔誓,帶你破解...
    沈念sama閱讀 216,843評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件斤吐,死亡現(xiàn)場離奇詭異搔涝,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)和措,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門庄呈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人派阱,你說我怎么就攤上這事诬留。” “怎么了贫母?”我有些...
    開封第一講書人閱讀 163,187評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵文兑,是天一觀的道長。 經(jīng)常有香客問我腺劣,道長绿贞,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,264評(píng)論 1 292
  • 正文 為了忘掉前任橘原,我火速辦了婚禮樟蠕,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘靠柑。我一直安慰自己寨辩,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,289評(píng)論 6 390
  • 文/花漫 我一把揭開白布歼冰。 她就那樣靜靜地躺著靡狞,像睡著了一般。 火紅的嫁衣襯著肌膚如雪隔嫡。 梳的紋絲不亂的頭發(fā)上甸怕,一...
    開封第一講書人閱讀 51,231評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音腮恩,去河邊找鬼梢杭。 笑死,一個(gè)胖子當(dāng)著我的面吹牛秸滴,可吹牛的內(nèi)容都是我干的武契。 我是一名探鬼主播,決...
    沈念sama閱讀 40,116評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼荡含,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼咒唆!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起释液,我...
    開封第一講書人閱讀 38,945評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤全释,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后误债,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體浸船,經(jīng)...
    沈念sama閱讀 45,367評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡妄迁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,581評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了李命。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片登淘。...
    茶點(diǎn)故事閱讀 39,754評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖项戴,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情槽惫,我是刑警寧澤周叮,帶...
    沈念sama閱讀 35,458評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站界斜,受9級(jí)特大地震影響仿耽,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜各薇,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,068評(píng)論 3 327
  • 文/蒙蒙 一项贺、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧峭判,春花似錦开缎、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至疗认,卻和暖如春完残,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背横漏。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評(píng)論 1 269
  • 我被黑心中介騙來泰國打工谨设, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人缎浇。 一個(gè)月前我還...
    沈念sama閱讀 47,797評(píng)論 2 369
  • 正文 我出身青樓扎拣,卻偏偏與公主長得像,于是被迫代替她去往敵國和親素跺。 傳聞我的和親對(duì)象是個(gè)殘疾皇子鹏秋,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,654評(píng)論 2 354

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,709評(píng)論 0 9
  • 原文出處:南峰子的技術(shù)博客 Objective-C語言是一門動(dòng)態(tài)語言亡笑,它將很多靜態(tài)語言在編譯和鏈接時(shí)期做的事放到了...
    _燴面_閱讀 1,229評(píng)論 1 5
  • 我們常常會(huì)聽說 Objective-C 是一門動(dòng)態(tài)語言侣夷,那么這個(gè)「動(dòng)態(tài)」表現(xiàn)在哪呢?我想最主要的表現(xiàn)就是 Obje...
    Ethan_Struggle閱讀 2,192評(píng)論 0 7
  • 轉(zhuǎn)載:http://yulingtianxia.com/blog/2014/11/05/objective-c-r...
    F麥子閱讀 733評(píng)論 0 2
  • 這是預(yù)覽模式 一級(jí)標(biāo)題 二級(jí)標(biāo)題 三級(jí)標(biāo)題 四級(jí)標(biāo)題 五級(jí)標(biāo)題 六級(jí)標(biāo)題 文本1 文本2 文本3 另外一種格式 文...
    waderwu閱讀 138評(píng)論 0 0