已下大部分內(nèi)容參考于:
Objective-C Runtime
楊蕭玉的博客 Objective-C Runtime
深入理解Objective-C的Runtime機(jī)制
Objc Runtime筆記 by 戴銘
Objective-C對象之類對象和元類對象(一)
什么是runtime放祟?
Runtime(運(yùn)行時(shí))跪妥,是一套底層的 C 語言 API眉撵,其為 iOS內(nèi)部的核心 之一,我們平時(shí)編寫的 OC代碼,底層都是基于它來實(shí)現(xiàn)的 缠沈。
Objective-C 對象可以用C語言中的結(jié)構(gòu)體表示,而方法(methods)可以用C函數(shù)實(shí)現(xiàn)顷锰。這些結(jié)構(gòu)體和函數(shù)被runtime函數(shù)封裝后,Objective-C程序員可以在程序運(yùn)行時(shí)創(chuàng)建束世,檢查毁涉,修改類贫堰,對象和它們的方法其屏。
除了封裝,Objective-C runtime庫也負(fù)責(zé)找出方法的最終執(zhí)行代碼蛤袒。當(dāng)程序執(zhí)行[object doSomething]時(shí)汗盘,不會(huì)直接找到方法并調(diào)用隐孽。而是會(huì)發(fā)送一條消息(message)給對象(在這兒踢俄,我們通常叫它接收者)都办,如下例:
Person *person = [[Person alloc] init];
[person eat];
// 底層運(yùn)行時(shí)會(huì)被編譯器轉(zhuǎn)化為:
objc_msgSend(person, @selector(eat))
// 如果其還有參數(shù)比如:
[person eat:(id)arg...];
// 底層運(yùn)行時(shí)會(huì)被編譯器轉(zhuǎn)化為:
objc_msgSend(person, @selector(eat), arg1, arg2, ...)
在上面代碼中琳钉,person對象就是接收者,系統(tǒng)會(huì)給person對象發(fā)送一個(gè)@selector(eat)消息及皂。然后person對象(接收者)根據(jù)消息名'eat'去Person類中找eat方法的實(shí)現(xiàn)验烧,然后再執(zhí)行eat方法(具體怎么執(zhí)行,接下來會(huì)細(xì)說)倔监。
這種消息傳遞機(jī)制源于Smalltalk,(Objective-C根據(jù)Smalltalk發(fā)展而來浩习。)
Objective-C 擴(kuò)展了C語言谱秽,將Smalltalk
的消息傳遞機(jī)制加入到了C中,并加入了面向?qū)ο筇匦浴?br>
Runtime是開源
的,可以去Apple的Open Source下載;
Objective-C
Objective-C 是一個(gè)動(dòng)態(tài)語言近哟,這意味著它不僅需要一個(gè)編譯器疯淫,也需要一個(gè)運(yùn)行時(shí)系統(tǒng)來動(dòng)態(tài)得創(chuàng)建類和對象熙掺、進(jìn)行消息傳遞和轉(zhuǎn)發(fā)币绩。理解 Objective-C 的 Runtime 機(jī)制可以幫我們更好的了解這個(gè)語言,適當(dāng)?shù)臅r(shí)候還能對語言進(jìn)行擴(kuò)展董瞻,從系統(tǒng)層面解決項(xiàng)目中的一些設(shè)計(jì)或技術(shù)問題。了解 Runtime 固额,要先了解它的核心 - 消息傳遞 (Messaging)。
Messageing
I'm sorry that I long ago coined the term "objects" for this topic because it gets many people to focus on the lesser idea.
The big idea is "messaging" - that is what the kernal of Smalltalk/Squeak is all about (and it's something that was never quite completed in our Xerox PARC phase).
Alan Kay反復(fù)強(qiáng)調(diào)消息傳遞(message-passing)是Smalltalk最重要的部分,objects(面向?qū)ο?只是lesser idea,messaging(消息傳遞)才是big idea 。
先來看看Runtime的一些數(shù)據(jù)結(jié)構(gòu)吧
Runtime數(shù)據(jù)結(jié)構(gòu)
還記得上文中提到的objc_msgSend:方法吧隧饼,來看看它的定義:
id objc_msgSend ( id self, SEL op, ... );
解釋一下這個(gè)函數(shù)的使用:
- 后面用了省略號(hào),說明此函數(shù)參數(shù)不確定,可以傳多個(gè)參數(shù),使用objc_msgSend函數(shù)時(shí)可能會(huì)報(bào)錯(cuò)(Too many arguments to function call,expected 0,hava...)拐格,如下圖是解決方案
解決方案
將Enable Strict Checking of objc_msgSend Calls 設(shè)置為 No刑赶。當(dāng)對象調(diào)用一個(gè)方法時(shí)捏浊,底層實(shí)際調(diào)用的就是這個(gè)函數(shù)(objc_msgSend),self代表方法調(diào)用者,
[object test]; == objc_msgSend(object,@selector(test));
初略介紹下objc_msgSend執(zhí)行流程:
執(zhí)行流程
下面將會(huì)逐漸展開介紹一些術(shù)語撞叨,其實(shí)它們都對應(yīng)著數(shù)據(jù)結(jié)構(gòu)金踪。
SEL
objc_msgSend函數(shù)第二個(gè)參數(shù)類型為SEL浊洞,在OC中它是用來表示@selector()類型的(Swift中是Selector類)。selector是方法選擇器沛申,可以理解為區(qū)分方法的 ID,而這個(gè) ID 的數(shù)據(jù)結(jié)構(gòu)是SEL:
typedef struct objc_selector *SEL;
其實(shí)它就是個(gè)映射到方法的C字符串姐军,你可以用 Objc 編譯器命令@selector()或者 Runtime 系統(tǒng)的sel_registerName函數(shù)來獲得一個(gè)SEL類型的方法選擇器铁材。如果你知道selector對應(yīng)的方法名是什么,可以通過NSString NSStringFromSelector(SEL aSelector)方法將SEL*轉(zhuǎn)化為字符串奕锌,再用NSLog打印著觉。
//返回給定選擇器指定的方法的名稱
const char * sel_getName ( SEL sel );
//在Objective-C Runtime系統(tǒng)中注冊一個(gè)方法,將方法名映射到一個(gè)選擇器惊暴,并返回這個(gè)選擇器
SEL sel_registerName ( const char *str );
//在Objective-C Runtime系統(tǒng)中注冊一個(gè)方法
SEL sel_getUid ( const char *str );
//比較兩個(gè)選擇器
BOOL sel_isEqual ( SEL lhs, SEL rhs );
上述函數(shù)使用案例:
int main(int argc, const char * argv[]) {
@autoreleasepool {
const char *name = [@"test" UTF8String];
SEL sel = sel_registerName(name);
NSLog(@"%s", sel_getName(sel));
NSLog(@"%p", sel);
SEL sel1 = @selector(test);
NSLog(@"%s", sel_getName(sel1));
NSLog(@"%p", sel1);
}
return 0;
}
控制臺(tái)打颖稹:
2016-04-14 16:53:22.446 Messaging[73956:10264811] test
2016-04-14 16:53:22.447 Messaging[73956:10264811] 0x7fff978441c8
2016-04-14 16:53:22.447 Messaging[73956:10264811] test
2016-04-14 16:53:22.447 Messaging[73956:10264811] 0x7fff978441c8
疑問:為什么sel的值和sel1的值相等?
因?yàn)?strong>不同類不管它們是父類與子類的關(guān)系辽话,還是之間沒有這種關(guān)系肄鸽,它們相同名字的方法所對應(yīng)的方法選擇器(SEL)是相同的(即共用同一個(gè)SEL),即使方法名相同而變量類型不同也會(huì)導(dǎo)致它們具有相同的方法選擇器.正好證明上圖中的@selector(test)是同一個(gè)SEL油啤。
每一個(gè)方法都有一個(gè)對應(yīng)SEL(也有可能多個(gè)方法對應(yīng)同一個(gè)SEL)典徘。編譯器會(huì)根據(jù)每個(gè)方法的方法名為那個(gè)方法生成唯一的SEL。這些SEL組成了一個(gè)Set集合益咬,當(dāng)我們在這個(gè)集合中查找某個(gè)方法時(shí)逮诲,只需要去找這個(gè)方法對應(yīng)的SEL即可。SEL實(shí)際上就是根據(jù)方法名hash化了的一個(gè)字符串幽告,而對于字符串的比較僅僅需要比較他們的地址就可以了
如在某個(gè)類中定義以下兩個(gè)方法:
這樣的定義被認(rèn)為是一種編譯錯(cuò)誤.
解釋:
因?yàn)閮蓚€(gè)方法名相同梅鹦,則@selector相同(共用一個(gè)@selector),不同類的實(shí)例對象執(zhí)行相同的selector時(shí),會(huì)在各自的方法列表中去根據(jù)selector去尋找自己對應(yīng)的
IMP
(方法的實(shí)現(xiàn))冗锁。當(dāng)此(圖中定義的類)類的一個(gè)實(shí)例調(diào)用setWidth:方法時(shí)齐唆,發(fā)現(xiàn)類中此方法有兩個(gè)IMP
(找一個(gè)方法的實(shí)現(xiàn)是根據(jù)方法名找的,跟方法的參數(shù)類型沒關(guān)系
)冻河,這樣系統(tǒng)就不知道執(zhí)行哪一個(gè)了蝶念,所以編譯器禁止在一個(gè)類中定義兩個(gè)同名的方法。所以在Objective-C同一個(gè)類(及類的繼承體系)中芋绸,不能存在2個(gè)同名的方法媒殉,即使參數(shù)類型不同也不行。
這一點(diǎn)與Java不同摔敛,Java中可以定義同名廷蓉,不同參(參數(shù)類型或者參數(shù)個(gè)數(shù)不同)的方法。
本質(zhì)上,SEL只是一個(gè)指向方法的key(類似一個(gè)字典桃犬,根據(jù)key找value刹悴,在這里SEL相當(dāng)于一個(gè)key,IMP相當(dāng)于一個(gè)Value)(準(zhǔn)確的說攒暇,SEL
只是一個(gè)根據(jù)方法名hash化了的KEY值土匀,能唯一代表一個(gè)方法),它的存在只是為了加快方法的查詢速度形用。這個(gè)查找過程我們將在下面討論就轧。
我們可以在運(yùn)行時(shí)添加新的selector,也可以在運(yùn)行時(shí)獲取已存在的selector田度,我們可以通過下面三種方法來獲取SEL:
- sel_registerName函數(shù)
上文已介紹
- Objective-C編譯器提供的@selector()
SEL sel = @selector(test);
- NSSelectorFromString()方法
SEL sel = NSSelectorFromString(@"test");
IMP
上文已提到IMP
,現(xiàn)在就來說說IMP
吧妒御。
IMP實(shí)際上是一個(gè)函數(shù)指針(方法最終是以函數(shù)的形式調(diào)用),指向函數(shù)實(shí)現(xiàn)的首地址(通俗理解就是镇饺,IMP
代表著.m
文件中方法的實(shí)現(xiàn))乎莉。這個(gè)被指向的方法包含一個(gè)接收消息的對象id,調(diào)用方法的SEL奸笤,以及一些方法參數(shù)惋啃,并返回一個(gè)id。其定義如下:
id (*IMP)(id, SEL, ...);
當(dāng)你向某個(gè)對象發(fā)送一條信息监右,消息接收者(對象)通過SEL獲得它所對應(yīng)的IMP边灭,在取得了函數(shù)指針之后,也就意味著我們?nèi)〉昧诵枰獔?zhí)行方法的代碼入口秸侣,這樣我們就可以像普通的C語言函數(shù)調(diào)用一樣使用這個(gè)函數(shù)指針存筏。
isa
Objective-C是一門面向?qū)ο蟮木幊陶Z言宠互。每一個(gè)對象都是一個(gè)類的實(shí)例味榛。其實(shí)在Objective-C中任何的類定義都是對象(類對象)
,在程序啟動(dòng)的時(shí)候任何類定義都對應(yīng)于一塊內(nèi)存予跌。在編譯的時(shí)候搏色,編譯器會(huì)給每一個(gè)類生成一個(gè)且只生成一個(gè)”描述其定義的對象”,也就是蘋果公司說的類對象(class object)
,他是一個(gè)單例(singleton)
, 而我們在C++等語言中所謂的對象,叫做實(shí)例對象(instance object)
券册。對于實(shí)例對象我們不難理解频轿,但類對象(class object)是干什么的呢?我們知道Objective-C是門動(dòng)態(tài)語言烁焙,因此程序里的所有實(shí)例對象(instace object)
都是在運(yùn)行時(shí)由Objective-C的運(yùn)行時(shí)庫生成的航邢,而這個(gè)類對象(class object)
就是運(yùn)行時(shí)庫用來創(chuàng)建實(shí)例對象(instance object)
的依據(jù)。
先來看看NSObject
,Class
,id
的定義骄蝇,再談?wù)?code>isa
@interface NSObject <NSObject> {
Class isa OBJC_ISA_AVAILABILITY;
}
typedef struct objc_class *Class;
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE;
const char *name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
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;
#endif
} OBJC2_UNAVAILABLE;
typedef struct objc_object *id;
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
從上述定義中可以看到運(yùn)行時(shí)一個(gè)類關(guān)聯(lián)了它的父類指針膳殷,類名,成員變量九火,方法赚窃,緩存册招,還有附屬的協(xié)議。
每一個(gè)類對象描述了一系列它的實(shí)例的特點(diǎn)勒极,包括成員變量的列表是掰,成員函數(shù)的列表等。每一個(gè)對象都可以接受消息辱匿,而對象能夠接收的消息列表是保存在它所對應(yīng)的類中键痛。
PS:OBJC2_UNAVAILABLE
之類的宏定義是蘋果在 Objc 中對系統(tǒng)運(yùn)行版本進(jìn)行約束的黑魔法,為的是兼容非Objective-C 2.0的遺留邏輯掀鹅,但我們?nèi)阅軓闹蝎@得一些有價(jià)值的信息散休,有興趣的可以查看源代碼。
在Objective-C語言的內(nèi)部乐尊,每個(gè)對象都有一個(gè)名為isa
的指針戚丸,指向該實(shí)例的類即類對象
(類對象其實(shí)就是一個(gè)objc_class結(jié)構(gòu)體
)。既然類也是一個(gè)對象扔嵌,那么類對象中應(yīng)該也有個(gè)isa指針限府,那么類對象的isa指針指向什么呢?
類對象的isa指向的是它的
元類對象(metaclass object)
,即類對象所屬類的對象
(關(guān)于metaclass下文會(huì)有解釋)痢缎。
類對象與類實(shí)例的關(guān)系
類對象的實(shí)質(zhì):
類對象是由編譯器創(chuàng)建的胁勺,即
在編譯時(shí)所謂的類,就是指類對象
(官方文檔中是這樣說的:The class object is the compiled version of the class
)独旷。任何直接或間接繼承了NSObject的類署穗,它的實(shí)例對象(instance objec)中都有一個(gè)isa
指針,指向這個(gè)實(shí)例對象的類對象(class object)嵌洼。這個(gè)類對象(class object)中存儲(chǔ)了關(guān)于這個(gè)實(shí)例對象(instace object)所屬的類的定義的一切
:包括變量案疲,方法,遵守的協(xié)議等等麻养。因此褐啡,類對象能訪問所有關(guān)于這個(gè)類的信息
,利用這些信息可以產(chǎn)生一個(gè)新的實(shí)例鳖昌,但是類對象不能訪問任何實(shí)例對象的內(nèi)容
(換一種方式理解就是:知道類有哪些屬性备畦,但不知道這些屬性的值
)。
區(qū)別
類對象保留了一個(gè)類實(shí)例的原型许昨,但它并不是實(shí)例本身懂盐。它沒有自己的實(shí)例變量,也不能執(zhí)行那些
類的實(shí)例
的方法(只有實(shí)例對象才可以執(zhí)行實(shí)例方法)糕档。然而莉恼,類的定義能包含那些特意為類對象準(zhǔn)備的方法–類方法
( 而不是的實(shí)例方法)。類對象從父類那里繼承類方法,就像實(shí)例從父類那里繼承實(shí)例方法一樣类垫。
元類對象-metaclass object
What is a meta-class in Objective-C?,中文版->翻譯 by Cocoabit;
元類對象的實(shí)質(zhì):
類對象是元類對象的一個(gè)實(shí)例司光!
元類描述了 一個(gè)類對象,就像類對象描述了普通對象一樣悉患。不同的是元類的方法列表是類方法的集合(類對象的方法列表是它對應(yīng)的實(shí)例對象的方法的集合)残家,根據(jù)類對象的選擇器來響應(yīng)。當(dāng)向一個(gè)類發(fā)送消息時(shí)售躁,objc_msgSend會(huì)通過類對象的isa指針定位到元類坞淮,并檢查元類的方法列表(包括父類)來決定調(diào)用哪個(gè)方法。元類代替了類對象描述了類方法陪捷,就像類對象代替了實(shí)例對象描述了實(shí)例化方法回窘。
很顯然,元類也是對象
市袖,也應(yīng)該是其他類的實(shí)例啡直,實(shí)際上元類是根元類(root class’s metaclass)的實(shí)例
,而根元類是其自身的實(shí)例
,即根元類的isa指針指向自身
苍碟。
這些理論可能有點(diǎn)繞酒觅,那就來張關(guān)系圖(該圖片來自這里) 說明一下吧,會(huì)更好理解:
Root Class 是指 NSObject
- 每個(gè)Class都有一個(gè)isa指針指向一個(gè)唯一的Meta Class
- 每一個(gè)Meta Class的isa指針都指向最上層的Meta Class(圖中的
Root class(NSObject
)的Meta Class
)
最上層的Meta Class的isa指針指向自己微峰,形成一個(gè)回路
- 每一個(gè)Meta Class的super class指針指向它原本Class的 Super Class的Meta Class舷丹。
但是最上層的Meta Class的 Super Class指向NSObject Class本身
- 最上層的Root class的super class指向 nil
當(dāng)一個(gè)消息發(fā)送給任何一個(gè)對象, 方法的檢查 從對象的 isa 指針指向的類開始(實(shí)例指向的是類對象)蜓肆,然后是父類颜凯。實(shí)例方法在類中定義, 類方法 在元類和根類中定義仗扬。(根類的元類就是根類自己)症概。在一些計(jì)算機(jī)語言的原理中,一個(gè)類和元類層次結(jié)構(gòu)可以更自由的組成厉颤,更深元類鏈和從單一的元類繼承的更多的實(shí)例化的類穴豫。
接下來看一下- (Class)class
和+ (Class)class
方法的源碼
// NSObject.mm
- (Class)class {
return object_getClass(self);
}
+ (Class)class {
return self;
}
// objc-class.mm
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
實(shí)例對象
調(diào)用- (Class)class
返回的是它isa指向的對象凡简,即類對象逼友,而類對象
調(diào)用+ (Class)class
返回的是類對象自己,因?yàn)轭悓ο笫菃卫某由?以Person為例):
Person p = [[Person alloc] init];
BOOL flag = [p class] == [Person class]
結(jié)果:flag = true
總結(jié):
當(dāng)一個(gè)類的實(shí)例調(diào)用
- (Class)class
方法帜乞,與這個(gè)類調(diào)用類方法+(Class)class
返回的是同一個(gè)東西。
id
關(guān)于id的描述摘自:楊蕭玉博客
objc_msgSend
第一個(gè)參數(shù)類型為id筐眷,大家對它都不陌生黎烈,它是一個(gè)指向類實(shí)例的指針:
typedef struct objc_object *id;
那objc_object又是啥呢:
struct objc_object { Class isa; };
objc_object結(jié)構(gòu)體包含一個(gè)isa
指針,根據(jù)isa
指針就可以順藤摸瓜找到對象所屬的類。
PS:isa
指針不總是指向?qū)嵗龑ο笏鶎俚念愓掌澹荒芤揽克鼇泶_定類型资溃,而是應(yīng)該用class方法來確定實(shí)例對象的類。因?yàn)镵VO的實(shí)現(xiàn)機(jī)理就是將被觀察對象的isa指針指向一個(gè)中間類而不是真實(shí)的類烈炭,這是一種叫做 isa-swizzling 的技術(shù)溶锭,詳見官方文檔
說了這么多,接下來就來說說objc_msgSend
到底是怎么工作的吧
在 Objective-C 中符隙,類趴捅、對象和方法都是一個(gè) C 的結(jié)構(gòu)體保存著
以objc_msgSend(target,@selector(test))
為例來說objc_msgSend的執(zhí)行流程吧:
- 1.檢查selector是否需要忽略。(ps: Mac開發(fā)中開啟GC就會(huì)忽略retain,release方法霹疫。)
- 2.檢查target是否為nil拱绑。如果為nil,直接cleanup丽蝎,然后return猎拨。(這就是我們可以向nil發(fā)送消息的原因。)
- 3.然后根據(jù)target的isa指針去它的Class中根據(jù)Selector去找IMP
尋找IMP的過程
- 1.先從當(dāng)前class的cache方法列表(cache methodLists)里去找
- 2.找到了屠阻,跳到對應(yīng)函數(shù)實(shí)現(xiàn)
- 3.沒找到迟几,就從class的方法列表(methodLists)里找
- 4.還找不到,就到super class的方法列表里找栏笆,直到找到基類(NSObject)為止
- 5.最后再找不到类腮,就會(huì)進(jìn)入動(dòng)態(tài)方法解析和消息轉(zhuǎn)發(fā)的機(jī)制。(關(guān)于動(dòng)態(tài)方法解析和轉(zhuǎn)發(fā)機(jī)制蛉加,后面文章會(huì)有介紹)
再來個(gè)圖文解說吧:
Objective-C方法調(diào)用流程:
圖中的methodLists是什么呢?
上文當(dāng)中已提到了objc_class
結(jié)構(gòu)體的定義,里面有個(gè)成員是
struct objc_method_list **methodLists;
根據(jù)名字和定義就可以猜到它是指向method的指針的集合(存的是指針
)针饥。
methodLists表示方法指針列表厂抽,它指向objc_method_list結(jié)構(gòu)體的二級指針,可以動(dòng)態(tài)修改*methodLists的值來添加成員方法丁眼,也是Category(關(guān)于Category會(huì)在后面的文章單獨(dú)介紹
)實(shí)現(xiàn)原理筷凤,同樣也解釋Category不能添加屬性的原因。
再來看看objc_method_list的定義吧:
struct objc_method_list {
struct objc_method_list *obsolete OBJC2_UNAVAILABLE;
int method_count OBJC2_UNAVAILABLE;
#ifdef __LP64__
int space OBJC2_UNAVAILABLE;
#endif
/* variable length structure */
struct objc_method method_list[1] OBJC2_UNAVAILABLE;
}
objc_method_list
就是用來存儲(chǔ)當(dāng)前類的方法指針鏈表苞七,objc_method存儲(chǔ)了類的某個(gè)方法的信息藐守。
Method
typedef struct objc_method *Method;
Method 是用來代表類中某個(gè)方法的類型,它實(shí)際就指向objc_method結(jié)構(gòu)體蹂风,如下:
struct objc_method {
SEL method_name OBJC2_UNAVAILABLE;
char *method_types OBJC2_UNAVAILABLE;
IMP method_imp OBJC2_UNAVAILABLE;
}
- method_types是個(gè)char指針卢厂,存儲(chǔ)著方法的參數(shù)類型和返回值類型。
- SEL method_name 和 IMP method_imp 就是我們上文提到的惠啄,所以我們可以理解為objc_class中 method list保存了一組SEL<->IMP的映射
調(diào)用方法時(shí)慎恒,系統(tǒng)不可能每次這樣去找IMP然后調(diào)用任内,這樣效率太低了
怎么解決呢?
使用緩存將調(diào)用過的方法緩存起來融柬,第二次調(diào)用的時(shí)候直接去緩存中找死嗦。
其實(shí)objc_class中cache就是用來干這個(gè)的。
那就來說說objc_class中的Cache
吧
Cache
先來看看它的定義吧:
typedef struct objc_cache *Cache OBJC2_UNAVAILABLE;
struct objc_cache {
/* total = mask + 1 */
unsigned int mask OBJC2_UNAVAILABLE;
unsigned int occupied OBJC2_UNAVAILABLE;
Method buckets[1] OBJC2_UNAVAILABLE;
};
- mask: 指定分配cache buckets的總數(shù)粒氧。在方法查找中越走,Runtime使用這個(gè)字段確定數(shù)組的索引位置
- occupied: 實(shí)際占用cache buckets的總數(shù)
- buckets: 指定Method數(shù)據(jù)結(jié)構(gòu)指針的數(shù)組。這個(gè)數(shù)組可能包含不超過mask+1個(gè)元素靠欢。需要注意的是廊敌,指針可能是NULL,表示這個(gè)緩存bucket沒有被占用门怪,另外被占用的bucket可能是不連續(xù)的骡澈。這個(gè)數(shù)組可能會(huì)隨著時(shí)間而增長。
objc_msgSend每調(diào)用一次方法后掷空,就會(huì)把該方法緩存到cache列表中肋殴,下次調(diào)用的時(shí)候,就直接優(yōu)先從cache列表中尋找坦弟,如果cache沒有护锤,才從methodLists中查找方法。
這樣可以優(yōu)化方法的調(diào)用性能酿傍,每當(dāng)實(shí)例對象接收到一個(gè)消息時(shí)烙懦,它不會(huì)直接根據(jù)isa 指針指向的類的方法列表中遍歷查找能夠響應(yīng)的方法,因?yàn)槊看味家檎倚侍土顺喑矗莾?yōu)先在 Cache 中查找氯析。
Runtime 系統(tǒng)會(huì)把被調(diào)用的方法存到 Cache 中,如果一個(gè)方法被調(diào)用莺褒,那么它有可能今后還會(huì)被調(diào)用掩缓,下次查找的時(shí)候就會(huì)效率更高。就像計(jì)算機(jī)組成原理中 CPU 繞過主存先訪問 Cache 一樣遵岩。
如果想更深的理解cache,可參考:深入理解Objective-C:方法緩存 (write by 美團(tuán)點(diǎn)評技術(shù)團(tuán)隊(duì))
下一篇文章會(huì)詳細(xì)介紹objc_msg...系列函數(shù)
,消息轉(zhuǎn)發(fā)
你辣、動(dòng)態(tài)解析
、method Swizzling
,NSInvacation使用
尘执。
如有不對的地方舍哄,請指正,謝謝正卧!