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)過程
- 通過isa去找到對應(yīng)的
objc_class; - 在
objc_class的methodLists中找到我們調(diào)用的方法,然后執(zhí)行。
Meta Class 元類
Objective-C中,類也被設(shè)計為一個對象。
調(diào)用對象類方法的實現(xiàn)過程(不考慮繼承)
- 通過對象的
isa指針找到對應(yīng)的類; - 通過類的
isa指針找到對應(yīng)元類; - 在元類的
methodList中,找到對應(yīng)的方法,然后執(zhí)行。

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建立了SEL和IMP的關(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ā)送

objc_msgSend函數(shù)的調(diào)用過程:
- 第一步:檢測這個
selector是不是要忽略的。 - 第二步:檢測這個
target是不是nil對象。nil對象發(fā)送任何一個消息都會被忽略掉。 - 第三步:
1.調(diào)用實例方法時,它會首先在自身isa指針指向的類(class)methodLists中查找該方法,如果找不到則會通過class的super_class指針找到父類的類對象結(jié)構(gòu)體,然后從methodLists中查找該方法,如果仍然找不到,則繼續(xù)通過super_class向上一級父類結(jié)構(gòu)體中查找,直至根class;
2.當我們調(diào)用某個某個類方法時,它會首先通過自己的isa指針找到metaclass,并從其中methodLists中查找該類方法,如果找不到則會通過metaclass的super_class指針找到父類的metaclass對象結(jié)構(gòu)體,然后從methodLists中查找該方法,如果仍然找不到,則繼續(xù)通過super_class向上一級父類結(jié)構(gòu)體中查找,直至根metaclass; - 第四部:前三部都找不到就會進入動態(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ī)操作
-
Method Swizzling方法交換 - 獲取所有屬性和方法
Runtime的應(yīng)用場景
- AOP面向切面編程(對業(yè)務(wù)邏輯進行分離,降低耦合度)
- 字典轉(zhuǎn)模型
- 進行歸解檔
- 逆向開發(fā)
- 熱修復(fù)
新手也看得懂的 iOS Runtime 教程
RSSwizzle源碼解析
Objective-C Runtime 1小時入門教程
深入理解Objective-C:Category