Objective-C Runtime之Messaging

已下大部分內(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è)方法:


test

這樣的定義被認(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ì)更好理解:

關(guān)系圖

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使用尘执。

如有不對的地方舍哄,請指正,謝謝正卧!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蠢熄,一起剝皮案震驚了整個(gè)濱河市跪解,隨后出現(xiàn)的幾起案子炉旷,更是在濱河造成了極大的恐慌签孔,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件窘行,死亡現(xiàn)場離奇詭異饥追,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)罐盔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進(jìn)店門但绕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人惶看,你說我怎么就攤上這事捏顺。” “怎么了纬黎?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵幅骄,是天一觀的道長。 經(jīng)常有香客問我本今,道長拆座,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任冠息,我火速辦了婚禮挪凑,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘逛艰。我一直安慰自己躏碳,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布散怖。 她就那樣靜靜地躺著唐断,像睡著了一般。 火紅的嫁衣襯著肌膚如雪杭抠。 梳的紋絲不亂的頭發(fā)上脸甘,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天,我揣著相機(jī)與錄音偏灿,去河邊找鬼丹诀。 笑死,一個(gè)胖子當(dāng)著我的面吹牛翁垂,可吹牛的內(nèi)容都是我干的铆遭。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼沿猜,長吁一口氣:“原來是場噩夢啊……” “哼枚荣!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起啼肩,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤橄妆,失蹤者是張志新(化名)和其女友劉穎衙伶,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體害碾,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡矢劲,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了慌随。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片芬沉。...
    茶點(diǎn)故事閱讀 40,096評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖阁猜,靈堂內(nèi)的尸體忽然破棺而出丸逸,到底是詐尸還是另有隱情,我是刑警寧澤剃袍,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布椭员,位于F島的核電站,受9級特大地震影響笛园,放射性物質(zhì)發(fā)生泄漏隘击。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一研铆、第九天 我趴在偏房一處隱蔽的房頂上張望埋同。 院中可真熱鬧,春花似錦棵红、人聲如沸凶赁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽虱肄。三九已至,卻和暖如春交煞,著一層夾襖步出監(jiān)牢的瞬間咏窿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工素征, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留集嵌,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓御毅,卻偏偏與公主長得像根欧,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子端蛆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評論 2 355

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