Runtime

Runtime是什么

將一些在編譯、鏈接過程中的工作,放到運行階段,因此Objective-C為動態(tài)語言

Runtime是一個庫,這個庫使我們可以在程序運行時創(chuàng)建對象、檢查對象、修改類和對象的方法。

Runtime是怎么工作的

Class和Object

objc.h中,Class被定義為指向objc_class的指針,定義如下:

typedef struct objc_class *Class;

objc_class是一個結(jié)構(gòu)體,在runtime.h中的定義如下:

struct objc_class {
    // isa指針指向的類結(jié)構(gòu)稱為metaclass,其中存放著static類型的成員變量與static類型的方法(“+”開頭的方法)
    Class isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    // 指向該類的父類的指針,如果該類是根類(如NSObject或NSProxy),那么super_class就為nil。
    Class super_class                                        OBJC2_UNAVAILABLE; // 父類
    const char *name                                         OBJC2_UNAVAILABLE; // 類名
    long version                                             OBJC2_UNAVAILABLE; // 類的版本信息,可以通過runtime函數(shù)class_setVersion或者class_getVersion進行修改、讀取
    long info                                                OBJC2_UNAVAILABLE; // 類信息,供運行時期使用的一些位標識、如CLS_CLASS(0x1L)表示該類為普通 class,其中包含實例方法和變量;CLS_META(0x2L)表示該類為metaclass,其中包含類方法;
    long instance_size                                       OBJC2_UNAVAILABLE; // 該類的實例變量大?。ò◤母割惱^承下來的實例變量)
    struct objc_ivar_list *ivars                             OBJC2_UNAVAILABLE; // 該類的成員變量地址列表
    struct objc_method_list **methodLists                    OBJC2_UNAVAILABLE; // 方法的地址列表,與info的一些標識位有關(guān),如CLS_CLASS(0x1L)則存儲實例方法,如CLS_META(0x2L),則存儲類方法;
    struct objc_cache *cache                                 OBJC2_UNAVAILABLE; // 緩存最近使用的方法列表,用于提升效率
    struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE; // 存儲該類聲明遵守的協(xié)議
#endif

} OBJC2_UNAVAILABLE;

一個類包含了自身所有的成員變量(ivars)、所有的方法(methodLists)、實現(xiàn)的協(xié)議(protocols

isa的定義如下:

struct objc_object {
    // 是一個指向objc_class結(jié)構(gòu)體的指針
    // objc_object(實例對象)中isa指針指向的類結(jié)構(gòu)稱為class(也就是該對象所屬的類)其中存放著普通成員變量與動態(tài)方法(“-”開頭的方法)
    Class isa  OBJC_ISA_AVAILABILITY;
};

一個對象唯一保存的信息就是它的Class的地址

調(diào)用對象方法的實現(xiàn)過程

  1. 通過isa去找到對應(yīng)的objc_class;
  2. objc_classmethodLists中找到我們調(diào)用的方法,然后執(zhí)行。

Meta Class 元類

Objective-C中,類也被設(shè)計為一個對象。

調(diào)用對象類方法的實現(xiàn)過程(不考慮繼承)

  1. 通過對象的isa指針找到對應(yīng)的類;
  2. 通過類的isa指針找到對應(yīng)元類;
  3. 在元類的methodList中,找到對應(yīng)的方法,然后執(zhí)行。
所有的metaclass中isa指針都是指向根metaclass,而根metaclass則指向自身。根metaclass是通過繼承根類產(chǎn)生的,與根class結(jié)構(gòu)體成員一致,不同的是根metaclass的isa指針指向自身。

Method

定義如下:

struct objc_method {
    SEL method_name                                          OBJC2_UNAVAILABLE; // 方法名
    char *method_types                                       OBJC2_UNAVAILABLE; // 方法類型
    IMP method_imp                                           OBJC2_UNAVAILABLE; // 方法實現(xiàn)
}

SEL是一個指向objc_selector的指針,而非objc_selector在頭文件中找不到明確的定義。不過是一個保存方法名的字符串。

IMP函數(shù)指針:找到函數(shù)地址,然后執(zhí)行函數(shù)。

typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...)

id對于實例方法來說,self保存了當前對象的地址;對于類方法來說,self保存了當前對應(yīng)類對象的地址;后面的省略號即是參數(shù)列表。

Method建立了SELIMP的關(guān)聯(lián),當對一個對象發(fā)送消息時,會通過給出的SEL去找到IMP,然后執(zhí)行。

objc_msgSend(id _Nullable self, SEL _Nonnull op, ...)

當向一個對象發(fā)送消息時,會去這個類methodLists中查找對應(yīng)的SEL,如果查不到,則通過super_class指針找到父類,再去父類的methodLists中查找,層層遞進。最后仍然找不到,才走拋異常流程。

攔截調(diào)用和消息轉(zhuǎn)發(fā)流程
重寫

resolveClassMethod:
resolveInstanceMethod:

消息發(fā)送

基本的消息發(fā)送框架

objc_msgSend函數(shù)的調(diào)用過程:

  • 第一步:檢測這個selector是不是要忽略的。
  • 第二步:檢測這個target是不是nil對象。nil對象發(fā)送任何一個消息都會被忽略掉。
  • 第三步:
    1.調(diào)用實例方法時,它會首先在自身isa指針指向的類(classmethodLists中查找該方法,如果找不到則會通過classsuper_class指針找到父類的類對象結(jié)構(gòu)體,然后從methodLists中查找該方法,如果仍然找不到,則繼續(xù)通過super_class向上一級父類結(jié)構(gòu)體中查找,直至根class;
    2.當我們調(diào)用某個某個類方法時,它會首先通過自己的isa指針找到metaclass,并從其中methodLists中查找該類方法,如果找不到則會通過metaclasssuper_class指針找到父類的metaclass對象結(jié)構(gòu)體,然后從methodLists中查找該方法,如果仍然找不到,則繼續(xù)通過super_class向上一級父類結(jié)構(gòu)體中查找,直至根metaclass;
  • 第四部:前三部都找不到就會進入動態(tài)方法解析(看下文)。
動態(tài)方法解析
  • 第一步:通過resolveInstanceMethod:方法決定是否動態(tài)添加方法。如果返回Yes則通過class_addMethod動態(tài)添加方法,消息得到處理,結(jié)束;如果返回NO,則進入下一步;

  • 第二步:這步會進入forwardingTargetForSelector:方法,用于指定備選對象響應(yīng)這個selector,不能指定為self。如果返回某個對象則會調(diào)用對象的方法,結(jié)束。如果返回nil,則進入第三部;

  • 第三部:這步我們要通過methodSignatureForSelector:方法簽名,如果返回nil,則消息無法處理。如果返回methodSignature`,則進入下一步;

  • 第四部:這步調(diào)用forwardInvocation:方法,我們可以通過 anInvocation對象做很多處理,比如修改實現(xiàn)方法,修改響應(yīng)對象等,如果方法調(diào)用成功,則結(jié)束。如果失敗,則進入doesNotRecognizeSelector方法,若我們沒有實現(xiàn)這個方法,那么就會crash。

Category

struct objc_category {
    char *category_name                                      OBJC2_UNAVAILABLE; // 類別名稱
    char *class_name                                         OBJC2_UNAVAILABLE; // 類名
    struct objc_method_list *instance_methods                OBJC2_UNAVAILABLE; // 實例方法列表
    struct objc_method_list *class_methods                   OBJC2_UNAVAILABLE; // 類方法列表
    struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE; // 協(xié)議列表
}       

objc_category中包含對象方法列表、類方法列表、協(xié)議列表。

可以通過關(guān)聯(lián)對象的方式給類別添加可用的屬性

Runtime的常規(guī)操作

  1. Method Swizzling方法交換
  2. 獲取所有屬性和方法

Runtime的應(yīng)用場景

  1. AOP面向切面編程(對業(yè)務(wù)邏輯進行分離,降低耦合度)
  2. 字典轉(zhuǎn)模型
  3. 進行歸解檔
  4. 逆向開發(fā)
  5. 熱修復(fù)

新手也看得懂的 iOS Runtime 教程
RSSwizzle源碼解析
Objective-C Runtime 1小時入門教程
深入理解Objective-C:Category

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,058評論 0 9
  • 我們常常會聽說 Objective-C 是一門動態(tài)語言,那么這個「動態(tài)」表現(xiàn)在哪呢?我想最主要的表現(xiàn)就是 Obje...
    Ethan_Struggle閱讀 2,339評論 0 7
  • 轉(zhuǎn)載:http://yulingtianxia.com/blog/2014/11/05/objective-c-r...
    F麥子閱讀 833評論 0 2
  • Objective-C語言是一門動態(tài)語言,他將很多靜態(tài)語言在編譯和鏈接時期做的事情放到了運行時來處理。這種動態(tài)語言...
    tigger丨閱讀 1,592評論 0 8
  • 2018年3月31號,晉城市心理學會第二小組第二次成長團體課如期舉行。本次主持人是張海燕老師?;顒又?,張老...
    rainbow琴閱讀 117評論 0 0

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