Runtime

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

運(yùn)行時(shí)是OC動(dòng)態(tài)性得以實(shí)現(xiàn)的一個(gè)機(jī)制映之,OC以一個(gè)動(dòng)態(tài)語(yǔ)言,把靜態(tài)語(yǔ)言編譯和鏈接的事情放到了運(yùn)行時(shí)來(lái)處理怜奖,但是怎么處理呢浑测,運(yùn)行時(shí)機(jī)制就是處理這個(gè)事情的,它是一套用C和匯編編寫(xiě)的API。
并且蘋(píng)果開(kāi)源了API迁央, 其中主要在文件runtime.h 和 message.h中

核心概念

類(lèi)的本質(zhì)
objc_class 和 Class

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif

}

// Class  結(jié)構(gòu)體指針掷匠,  指向某個(gè)類(lèi)實(shí)例, 表示這個(gè)類(lèi)
typedef struct objc_class *Class;

類(lèi)的本質(zhì)岖圈,或者說(shuō)數(shù)據(jù)格式就是結(jié)構(gòu)體讹语, 一個(gè)結(jié)構(gòu)體變量就是一個(gè)類(lèi)對(duì)象,或者說(shuō)實(shí)例蜂科。

objc_object 和 id

struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};
typedef struct objc_object *id;

objc_selector 和 SEL

typedef struct objc_selector *SEL;

IMP 可以理解為函數(shù)指針顽决, 指向函數(shù)實(shí)現(xiàn)首地址

#if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ ); 
#else
typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...); 
#endif
/// 方法    Method  結(jié)構(gòu)體指針,  一個(gè)方法包含 方法名SEL导匣,  方法類(lèi)型 和 方法的實(shí)現(xiàn)IMP
struct objc_method {
    SEL _Nonnull method_name                                 OBJC2_UNAVAILABLE;
    char * _Nullable method_types                            OBJC2_UNAVAILABLE;
    IMP _Nonnull method_imp                                  OBJC2_UNAVAILABLE;
} 
typedef struct objc_method *Method;


/// 實(shí)例變量
struct objc_ivar {
    char * _Nullable ivar_name         // 變量名                      OBJC2_UNAVAILABLE;
    char * _Nullable ivar_type              // 變量的類(lèi)型編碼                  OBJC2_UNAVAILABLE;
    int ivar_offset                                          OBJC2_UNAVAILABLE;
#ifdef __LP64__
    int space                                                OBJC2_UNAVAILABLE;
#endif
}
typedef struct objc_ivar *Ivar;

/// 分類(lèi)  包括分類(lèi)名擎值,  類(lèi)名,實(shí)例方法列表逐抑,類(lèi)方法列表和協(xié)議列表
struct objc_category {
    char * _Nonnull category_name                            OBJC2_UNAVAILABLE;
    char * _Nonnull class_name                               OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable instance_methods     OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable class_methods        OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
}
typedef struct objc_category *Category;

/// 屬性
typedef struct objc_property *objc_property_t;

類(lèi),父類(lèi)屹蚊,元類(lèi)

/*
        1, 實(shí)例的類(lèi)其實(shí)也是對(duì)象厕氨, 叫做類(lèi)對(duì)象,區(qū)別就是類(lèi)對(duì)象在內(nèi)存中只有一份  類(lèi)對(duì)象的類(lèi)叫做元類(lèi)
        2汹粤,當(dāng)調(diào)用實(shí)例方法時(shí)命斧, 會(huì)去類(lèi)對(duì)象的方法列表中查找匹配
        3,當(dāng)調(diào)用類(lèi)方法時(shí)嘱兼,  會(huì)去類(lèi)的元類(lèi)中查找
        4国葬,觀察類(lèi)存儲(chǔ)結(jié)構(gòu)的定義,  發(fā)現(xiàn)有isa和  super兩個(gè)class類(lèi)型的數(shù)據(jù)芹壕,  其中isa指向所屬的類(lèi)汇四,super指向父類(lèi)
        5.每個(gè)實(shí)例對(duì)象的類(lèi)都是類(lèi)對(duì)象,每個(gè)類(lèi)對(duì)象的類(lèi)都是元類(lèi)對(duì)象踢涌,每個(gè)元類(lèi)對(duì)象的類(lèi)都是根元類(lèi)(root meta class的isa指向自身)
      6.類(lèi)對(duì)象的父類(lèi)最終繼承自根類(lèi)對(duì)象NSObject通孽,NSObject的父類(lèi)為nil
        7.元類(lèi)對(duì)象(包括根元類(lèi))的父類(lèi)最終繼承自根類(lèi)對(duì)象NSObject
     */
類(lèi)和元類(lèi)結(jié)構(gòu)圖.png

方法調(diào)用的本質(zhì)?
方法調(diào)用的本質(zhì)就是向方法調(diào)用者發(fā)送了一條消息睁壁,核心方法是
objc_msgSend(void /* id self, SEL op, ... */ ) , OC中的每一個(gè)方法調(diào)用會(huì)轉(zhuǎn)化成這個(gè)c函數(shù)調(diào)用背苦, 其中第一個(gè)參數(shù)是方法的調(diào)用者钦无, 第二個(gè)參數(shù)表示方法名痰哨, 之后是可變參數(shù)列表, 可以傳入調(diào)用方法需要的參數(shù)牍蜂。那么這個(gè)方法底層做了什么呢钳降, 它會(huì)去該實(shí)例的類(lèi)對(duì)象的方法列表中尋找同名的方法厚宰, 如果在本類(lèi)中找不到就去到父類(lèi)中尋找, 如果找到同名方法SEL或者Selector后遂填, 拿到對(duì)應(yīng)的IMP固阁,然后根據(jù)參數(shù)調(diào)用對(duì)應(yīng)的函數(shù)壤躲。如果找不到就會(huì)進(jìn)入消息轉(zhuǎn)發(fā)

有什么作用

利用runtime提供的API我們可以獲取所有已經(jīng)注冊(cè)的類(lèi),獲取指定類(lèi)的信息(包括名字备燃,所有實(shí)例變量碉克,屬性, 方法信息) 動(dòng)態(tài)的創(chuàng)建類(lèi)并齐, 給類(lèi)添加方法漏麦,屬性(assiociateObject)和 交換方法的實(shí)現(xiàn)(hook)
1, 獲取類(lèi)的所有實(shí)例變量(類(lèi)型 名字)其中實(shí)例變量包括類(lèi)中定義的實(shí)例變量和屬性生成的實(shí)例變量
2,獲取屬性
3况褪,獲取方法撕贞。 獲取對(duì)象方法是在本類(lèi)中查找,测垛, 獲取類(lèi)方法需要到元類(lèi)中查找
4捏膨,添加屬性。
4.1食侮,對(duì)于還沒(méi)有注冊(cè)的類(lèi) 添加屬性有相應(yīng)的函數(shù)号涯,但是屬性類(lèi)型編碼需要看一下
4.2, 對(duì)于已經(jīng)注冊(cè)過(guò)的類(lèi)锯七,如果想添加屬性的話链快,只能使用關(guān)聯(lián)對(duì)象了。對(duì)于還沒(méi)有注冊(cè)的類(lèi) 有相應(yīng)的函數(shù)可以添加屬性
5眉尸,添加方法 改變方法的實(shí)現(xiàn)
6域蜗,動(dòng)態(tài)的添加一個(gè)類(lèi) 創(chuàng)建實(shí)例
7,獲取實(shí)例變量 和 屬性區(qū)別
8噪猾, 用運(yùn)行時(shí)配合KVC 改變系統(tǒng)的私有屬性
詳見(jiàn)demo https://github.com/JTWang4778/RuntimeDemo

在Swift中使用和在OC中使用有什么區(qū)別

Swift代碼中已經(jīng)沒(méi)有了Objective-C的運(yùn)行時(shí)消息機(jī)制, 在代碼編譯時(shí)即確定了其實(shí)際調(diào)用的方法. 所以純粹的Swift類(lèi)和對(duì)象沒(méi)有辦法使用runtime, 更不存在method swizzling.
為了兼容Objective-C, 凡是繼承NSObject的類(lèi)都會(huì)保留其動(dòng)態(tài)性, 依然遵循Objective-C的?運(yùn)行時(shí)消息機(jī)制, 因此可以通過(guò)runtime獲取其屬性和方法, 實(shí)現(xiàn)method swizzling.

面向切面編程

APO霉祸, Aspect Oriented Programming面向切面編程, 可以通過(guò)預(yù)編譯方式和運(yùn)行期動(dòng)態(tài)代理實(shí)現(xiàn)在不修改源代碼的情況下給程序動(dòng)態(tài)統(tǒng)一添加功能的一種技術(shù)袱蜡。利用運(yùn)行時(shí)我們可以在IOS開(kāi)發(fā)中運(yùn)用面向切面編程的思路給工程統(tǒng)一添加某一項(xiàng)功能脉执。 典型的是給現(xiàn)有的類(lèi)添加方法和屬性,然后hook到原有實(shí)現(xiàn)添加處理戒劫。 有名的三方有FDFullscreenPopGesture 和 MLeaksFinder

關(guān)于runtime的幾個(gè)問(wèn)題

1, + class, -class 和 objc_getClass 作用一樣嗎半夷?
object_getClass(obj)返回的是obj中的isa指針;而[obj class]則分兩種情況:一是當(dāng)obj為實(shí)例對(duì)象時(shí)迅细,[obj class]中class是實(shí)例方法:- (Class)class巫橄,返回的obj對(duì)象中的isa指針;二是當(dāng)obj為類(lèi)對(duì)象(包括元類(lèi)和根類(lèi)以及根元類(lèi))時(shí)茵典,調(diào)用的是類(lèi)方法:+ (Class)class湘换,返回的結(jié)果為其本身。
http://www.reibang.com/p/ae5c32708bc6
2,isKindOfClass 和 isMemmberOfClass 區(qū)別

//    JTView *subInstance = [JTView new];
//    // 調(diào)用者是否為給定類(lèi)的實(shí)例或任何繼承自給定類(lèi)的實(shí)例彩倚。
//    BOOL asdf = [subInstance isKindOfClass:[JTView class]];
//    if (asdf) {
//        NSLog(@"是子類(lèi)");
//    }else {
//        NSLog(@"不是子類(lèi)");
//    }
//    // 是否是給定類(lèi)的實(shí)例
//    if ([subInstance isMemberOfClass:[JTView class]]) {
//        NSLog(@"是該類(lèi)的子類(lèi)");
//    }else {
//        NSLog(@"不是該類(lèi)的子類(lèi)");
//    }

3, 幾個(gè)面試題
http://blog.sunnyxx.com/2014/11/06/runtime-nuts/

4,+load 和 +initialize 方法

  • 調(diào)用方式不同筹我,load方法是在main方法執(zhí)行之前,直接調(diào)用函數(shù)帆离,而initialize走的是消息機(jī)制
  • 調(diào)用時(shí)機(jī)不同蔬蕊, load方法是在main方法執(zhí)行之前, 類(lèi)加載進(jìn)內(nèi)存的時(shí)候調(diào)用的哥谷,并且只要類(lèi)實(shí)現(xiàn)了load方法必定調(diào)用岸夯。而initialize是惰性調(diào)用, 只有第一次使用類(lèi)的時(shí)候才會(huì)調(diào)用
  • load方法的調(diào)用順序是(如果都實(shí)現(xiàn)的話)超類(lèi)们妥,子類(lèi)猜扮,分類(lèi),對(duì)于多個(gè)分類(lèi)的調(diào)用順序就不一定了监婶。如果沒(méi)有實(shí)現(xiàn)就不調(diào)用旅赢,不會(huì)調(diào)用父類(lèi)的load方法。也就是說(shuō)一個(gè)類(lèi)的load方法只調(diào)用一次惑惶,但是initialize可能調(diào)用多次煮盼。
  • initialize 是線程安全的, 如果子類(lèi)沒(méi)有實(shí)現(xiàn)就會(huì)調(diào)用父類(lèi)的方法
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末集惋,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子踩娘,更是在濱河造成了極大的恐慌刮刑,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,252評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件养渴,死亡現(xiàn)場(chǎng)離奇詭異雷绢,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)理卑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)翘紊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人藐唠,你說(shuō)我怎么就攤上這事帆疟。” “怎么了宇立?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,814評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵踪宠,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我妈嘹,道長(zhǎng)柳琢,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,869評(píng)論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮柬脸,結(jié)果婚禮上他去,老公的妹妹穿的比我還像新娘。我一直安慰自己倒堕,他們只是感情好灾测,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著涩馆,像睡著了一般行施。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上魂那,一...
    開(kāi)封第一講書(shū)人閱讀 52,475評(píng)論 1 312
  • 那天蛾号,我揣著相機(jī)與錄音,去河邊找鬼涯雅。 笑死鲜结,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的活逆。 我是一名探鬼主播精刷,決...
    沈念sama閱讀 41,010評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蔗候!你這毒婦竟也來(lái)了怒允?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,924評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤锈遥,失蹤者是張志新(化名)和其女友劉穎纫事,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體所灸,經(jīng)...
    沈念sama閱讀 46,469評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡丽惶,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了爬立。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片钾唬。...
    茶點(diǎn)故事閱讀 40,680評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖侠驯,靈堂內(nèi)的尸體忽然破棺而出抡秆,到底是詐尸還是另有隱情,我是刑警寧澤吟策,帶...
    沈念sama閱讀 36,362評(píng)論 5 351
  • 正文 年R本政府宣布琅轧,位于F島的核電站,受9級(jí)特大地震影響踊挠,放射性物質(zhì)發(fā)生泄漏乍桂。R本人自食惡果不足惜冲杀,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評(píng)論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望睹酌。 院中可真熱鬧权谁,春花似錦、人聲如沸憋沿。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,519評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)辐啄。三九已至采章,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間壶辜,已是汗流浹背悯舟。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,621評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留砸民,地道東北人抵怎。 一個(gè)月前我還...
    沈念sama閱讀 49,099評(píng)論 3 378
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像岭参,于是被迫代替她去往敵國(guó)和親反惕。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評(píng)論 2 361

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