Runtime簡介
運行時最主要的是消息機制
- 對于C語言彰触,函數(shù)調(diào)用在編譯的時候會決定調(diào)用哪個函數(shù)
- 對于OC函數(shù)命辖,屬于
動態(tài)調(diào)用過程
,在編譯的時候并不能決定真正調(diào)用哪個函數(shù)尔艇,只有在真正運行的時候才會根據(jù)函數(shù)的名稱找到對應的函數(shù)來調(diào)用, - 在編譯階段味廊,oc可以調(diào)用任何函數(shù)棠耕,即使這個函數(shù)并未實現(xiàn)余佛,只要聲明過就不會報錯
在編譯階段窍荧,C語言調(diào)用未實現(xiàn)的函數(shù)就會報錯 - 如果向某個對象傳遞消息,在底層所有的方法都是普通的C語言函數(shù)红氯,然而對象收到消息之后,究竟該調(diào)用哪個方法則完全取決于運行期決定,甚至可能在運行期改變喇嘱,這些特性使得OC變成一門真正的動態(tài)語言
- 在Runtime中,對象可以使用C語言中的結(jié)構(gòu)體表示腔丧,而方法可以用C函數(shù)實現(xiàn)作烟,另外在加上了額外的特性愉粤,這些結(jié)構(gòu)體和函數(shù)被Runtime函數(shù)封裝后拿撩,讓OC的面向?qū)ο缶幊套優(yōu)榭赡?/li>
Objective-C中的數(shù)據(jù)結(jié)構(gòu)
1.id
運行時系統(tǒng)如何知道某個對象的類型呢?對象類型并不是在編譯期就知道了影暴,而是要在運行期查找,OC有個特殊類型id,它可以表示OC的任意對象類型型宙,id類型定義在Runtime的頭文件中:
struct obje_object {
Class isa;
} *id;
由此可見,每個對象結(jié)構(gòu)體的首個成員變量是Class類的isa妆兑,該變量定義了對象所屬的類,通常指isa指針
objc_object
objc_object 是一個表示一個類實例的結(jié)構(gòu)體芯勘,它的定義如下(objc/objc.h):
struct objc_object{
Class isa OBJC_ISA_AVAILABILITY;
};
typedef struce objc_object *id;
可以看到谱姓,這個結(jié)構(gòu)體只有一個實體借尿,基指向其類的isa指針屉来。這樣,當我們向一個OC對象發(fā)送消息時茂契,運行時庫會根據(jù)實例對象的isa指針找到這個實例對象所屬的類,Runtime庫會在類的方法列表以及父類的方法列表中尋找與消息對應的selector指向的方法掉冶,找到后即運行這個方法脐雪。
2.Class
Class對象也定義在Runtime的頭文件中,查看objc/runtime.h中的objc_class結(jié)構(gòu)體:Objective-c中战秋,類是由Class類型來表示的,它實際是一個指向objc_class結(jié)構(gòu)體的指針脂信。
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;//類的版本信息,默認為0
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;//協(xié)議鏈表
#endif
} OBJC2_UNAVAILABLE;
下面說下Class 結(jié)構(gòu)體的幾個主要變量:
1.isa:結(jié)構(gòu)體的首個變量也是isa指針埋泵,這說明Class本身也是OC中的對象。isa指針非常重要,對象需要通過isa指針找到它的類规阀,類需要isa找到元類瘦麸,這在調(diào)用實例方法和類方法的時候起到重要作用谁撼。
2.super_class:結(jié)構(gòu)體里還有個變量是super_class,它定義了本類的超類滋饲。類對象所屬類型(isa指針所指向的類型)是另一個類,叫做元類箍鼓。
3.ivars:成員變量列表,類的成員都在ivars里面款咖。
4.methodLists:方法列表奄喂,類的實例方法都在methodLists里铐殃,類方法在元類的methodLists里面跨新。methodLists是一個指針的指針,通過修改該指針指向指針的值赘被,就可以動態(tài)的為某一個類添加成員方法肖揣。這就是Category實現(xiàn)的原理民假,同時也說明Category只可以為對象添加成員方法龙优,不能添加成員變量。
5.cache:方法緩存列表陋率,objc_msgSend(下文詳解)每調(diào)用一次方法后,就會把該方法緩存到cache列表中瓦糟,下次調(diào)用的時候赴蝇,會優(yōu)先從cache列表中尋找菩浙,如果cache沒有,才從methodLists中查找方法陆淀,提高效率先嬉。
元類(Meta Class)
meta-class 是一個類對象的類轧苫。在上面我們提到疫蔓,所有的類自身也是一個對象,我們可以向這個對象發(fā)送消息(即調(diào)用類方法)岔乔。既然是對象,那么它也是一個objc_object指針雏门,它包含一個指向其類的一個isa指針掸掏,那么,這個isa指針指向什么呢阅束?為了調(diào)用類方法,這個類的isa指針必須指向一個包含這個類方法的一個objc_class結(jié)構(gòu)體息裸。這就引出了meta-class的概念,meta-class中存儲著一個類的所有類方法年扩。所以,調(diào)用類方法的這個類對象的isa指針指向的就是meta-class 當我們向一個對象發(fā)送消息時厨幻,runtime會在這個對象所屬的這個類的方法列表中查找方法腿时;而向一個類發(fā)送消息時,會在這個類的meta-class的方法列表中查找批糟。
再深入一下,meta-class 也是一個類徽鼎,也可以向它發(fā)送一個消息弹惦,那么它的isa又是指向什么呢悄但?為了不讓這種結(jié)構(gòu)無限延伸下去,Objective-C的設計者讓所有的meta-class的isa指向基類的meta-class,以此作為他們的所屬類檐嚣。
即,任何NSObject繼承體系下的meta-class都使用NSObject的meta-class作為自己的所屬類报咳,而基類的meta-class的isa指針是指向它自己挖藏。
通過上面的描述暑刃,再加上對objc_class結(jié)構(gòu)體中super_class指針的分析,我們就可以描繪出類及相應meta-class類的一個繼承體系了岩臣,如下代碼
上圖 superclass指針代表繼承關系宵膨,isa指針代表實例所屬的類。類也是一個對象辟躏,它是另一個類的實例,這個就是”元類“会涎,元類里面保存了類方法的列表,類里面保存了實例方法的列表末秃。實例對象的isa指向類籽御,類對象的isa指向元類,元類對象的isa指向一個根元類(root metaclass)技掏。所有子類的元類都繼承父類的元類,換而言之哑梳,類對象和元類對象有同樣的繼承關系。
Class是一個指向objc_class結(jié)構(gòu)體的指針哪工,而id是一個指向objc_object結(jié)構(gòu)體的指針,其中的isa是一個指向objc_class結(jié)構(gòu)體的指針雁比。其中的id就是我們說的對象撤嫩,Class就是我們所說的類。isa指針不總是指向?qū)嵗龑ο笏鶎俚念愋蛉粒荒芤揽克鼇泶_定類型,而是應該用isKindOfClass:方法來確定實例對象的類程奠。因為KVO的實現(xiàn)機制就是將被觀察對象的isa指針指向一個中間類而不是真實的類。
Category
Category是表示一個指向分類的結(jié)構(gòu)體的指針己沛,其定義如下:
/// An opaque type that represents a category.
typedef struct objc_category *Category;
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;
}
這個結(jié)構(gòu)體主要包含了分類定義的實例方法與類方法距境,其中instance_methods列表是objc_class中方法列表的一個子集,而class_methods列表是元類方法列表的一個子集垫桂。可發(fā)現(xiàn)诬滩,類別中沒有ivar成員變量指針,也就意味著:類別中不能夠添加實例變量和屬性
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; // 該類的成員變量鏈表
3.SEL
-
方法交換(method swizzing)
在Objctive-C中蒙挑,對象收到消息后愚臀,究竟會調(diào)用哪種方法需要在運行期才能解析出來忆蚀。查找消息的唯一依據(jù)是選擇子(selector),選擇子(selector)與相應的方法(IMP)對應姑裂,利用Objective-C的動態(tài)特性,可以實現(xiàn)在運行時偷換選擇子(selector)對應的方法實現(xiàn)欣鳖,這就是方法交換 (method swizzing).
每個類都有一個方法列表茴厉,存放著selector的名字和方法實現(xiàn)的映射關系泽台。IMP有點類似函數(shù)指針,指向具體的Method實現(xiàn)稻爬。
我們可以新增選擇子蜕依,也可以改變某個選擇子所對應的IMP,還可以交換兩個選擇子所映射到的指針样眠。
- Objective-C中提供了三種API來動態(tài)替換類方法或?qū)嵗椒ǖ膶崿F(xiàn):
1.class_replaceMethod
替換類方法的定義。
class_replaceMethod(Class cls,SEL name,IMP imp,const char *types)
2.method_exchangeImplementations
交換兩個方法的實現(xiàn)辫秧。
method_exchangeImplementations(Method m1,Method m2)
3.method_setImplementation
設置一個方法的實現(xiàn)
method_setImplementation(Method m,IMP imp)
先說這三個方法的區(qū)別:
-
class_replaceMethod
:當類中沒有想替換的原方法時厢塘,該方法調(diào)用class_addMethod
來為類增加一個新方法茶没,也正因如此晚碾,class_replaceMethod
在調(diào)用時需傳入types參數(shù),而其余兩個缺不需要笛求。 -
method_exchangeImplementations
: 內(nèi)部實現(xiàn)就是調(diào)用了兩次method_setImplemetation
方法糕簿。
再來看看他們的使用場景:
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
SEL originalSelector = @selector(willMoveToSuperview:);
SEL swizzledSelector = @selector(myWillMoveToSuperview:);
Method originalMethod = class_getInstanceMethod(self, originalSelector);
Method swizzledMethod = class_getInstanceMethod(self, swizzledSelector);
BOOL didAddMethod = class_addMethod(self,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod(self,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
- (void)myWillMoveToSuperview:(UIView *)newSuperview
{
NSLog(@"WillMoveToSuperview: %@", self);
[self myWillMoveToSuperview:newSuperview];
}
總結(jié)
class_replaceMethod
,當需要替換的方法有可能不存在是,可以考慮使用該方法懂诗。
method_exchangeImplementations
,當需要交換兩個方法時使用
method_setImplementation
是最簡單的用法植旧,當僅僅需要為一個方法設置其實現(xiàn)方式時實現(xiàn)。
4.Ivar
ivar 代表類中實例變量的類型病附,在Runtime的頭文件中的定義如下:
typedef struct objc_ivar *Ivar;
objc_ivar的定義如下:
struct objc_ivar {
char * _Nullable ivar_name OBJC2_UNAVAILABLE;
char * _Nullable ivar_type OBJC2_UNAVAILABLE;
int ivar_offset OBJC2_UNAVAILABLE;
#ifdef __LP64__
int space OBJC2_UNAVAILABLE;
#endif
}
class_copyIvarList(Class cls,unsigned int *outCount)
可以使用這個方法獲取某個類的成員變量列表
5.objc_property_t
objc_property_t是屬性亥鬓,在Runtime的頭文件中的定義如下:
typedef struct objc_property *objc_property_t;
class_copyPropertyList(Class cls, unsigned int *outCount)
可以使用這個方法獲取某個類的屬性列表。