Understanding Objective-C

Understanding Objective-C

本文翻譯原作者地址這里

動(dòng)態(tài)語言VS靜態(tài)語言

Objective-C是一個(gè)基于運(yùn)行時(shí)的語言柱恤,也就是說通過運(yùn)行時(shí)涯塔,Objective-C會(huì)延遲到編譯的時(shí)候再?zèng)Q定具體執(zhí)行的是什么。通過運(yùn)行時(shí)膜赃,當(dāng)你要重定向一個(gè)消息到一個(gè)合適的對象挺邀,或者有目的的交換兩個(gè)方法的實(shí)現(xiàn)提供了很大的靈活性。如果我們將C語言與其進(jìn)行對比跳座,C語言從一個(gè)main()方法開始這行端铛,然后便是根據(jù)你的邏輯自上而下設(shè)計(jì)和執(zhí)行。一個(gè)C語言的結(jié)構(gòu)體不能轉(zhuǎn)發(fā)請求到另一個(gè)目標(biāo)上執(zhí)行函數(shù)疲眷。
通常一個(gè)下面這樣的C程序

#include < stdio.h >
 
int main(int argc, const char **argv[])
{
        printf("Hello World!");
        return 0;
}

編譯器解析后是這樣的:

.text
 .align 4,0x90
 .globl _main
_main:
Leh_func_begin1:
 pushq %rbp
Llabel1:
 movq %rsp, %rbp
Llabel2:
 subq $16, %rsp
Llabel3:
 movq %rsi, %rax
 movl %edi, %ecx
 movl %ecx, -8(%rbp)
 movq %rax, -16(%rbp)
 xorb %al, %al
 leaq LC(%rip), %rcx
 movq %rcx, %rdi
 call _printf
 movl $0, -4(%rbp)
 movl -4(%rbp), %eax
 addq $16, %rsp
 popq %rbp
 ret
Leh_func_end1:
 .cstring
LC:
 .asciz "Hello World!"

最后通過一個(gè)庫連接到一起然后生成一個(gè)可執(zhí)行的程序禾蚕。與Objective-C不同的是盡管處理的是類似的代碼,但是編譯器編譯的時(shí)候是基于Objective-C 運(yùn)行時(shí)的庫來處理的狂丝。我們在Objective-C入門的時(shí)候都知道换淆,
[self doSomethingWithVar:var1]
是轉(zhuǎn)化為
objc_msgSend(self, @selector(doSomethingWithVar:), var1);
但是除此之外,我們不知道以后進(jìn)一步發(fā)生了些什么几颜。

什么是Objective-C的Runtime倍试?

Objective-C Runtime是一個(gè)Runtime庫,大部分是由C語言編寫的蛋哭,給C語言添加了面向?qū)ο蟮奶匦詣?chuàng)造了Objective-C县习。這些特性包括加載Class信息,方法分發(fā)谆趾,方法轉(zhuǎn)發(fā)等躁愿。Objective-C的runtime創(chuàng)建了所有的能夠使Objective-C能夠面向?qū)ο缶幊痰慕Y(jié)構(gòu)。

Objective-C 術(shù)語

為了方便我們更好的理解沪蓬,先了解一些屬于彤钟。Runtime到目前是有兩種:最新的Runtime 和歷史Rumtime。所有的64位Mac OS APPs以及所有的iOS APPs都是新的Runtime.其余的都是歷史Runtime跷叉。另外介紹兩種基本的方法類型逸雹。實(shí)例方法(以-開頭营搅,如- (void)doFoo;)通過類的實(shí)例操作。另外一個(gè)是類方法(+開頭,如+ (id)alloc;)
Objective-C方法與C語言函數(shù)相似

- (NSString *) movieTitle{
    return @"Hello World!";
}

選擇器在Objective-C中本質(zhì)上是一個(gè)C的數(shù)據(jù)結(jié)構(gòu)峡眶,來確定一個(gè)你所指定對象執(zhí)行的方法剧防。在Runtime中定義是下面這個(gè)樣子:
typedef struct objc_selector *SEL
用法是
SEL aSel = @selector(movieTitle)
消息
[target getMovieTitleForObject:obj]
一個(gè)Objective-C的消息是在'[]'內(nèi)的,其中包含你消息要發(fā)送的對象辫樱,你要執(zhí)行的方法,以及執(zhí)行方法所需要的參數(shù)俊庇。Objective-C消息發(fā)送與C語言函數(shù)調(diào)用不一樣的狮暑,區(qū)別在于在Objective-C中你給一個(gè)對象發(fā)送消息并不代表這個(gè)對象就會(huì)執(zhí)行這個(gè)消息。對象會(huì)檢查誰是消息的發(fā)送者并且在這個(gè)基礎(chǔ)之上執(zhí)行一個(gè)不同的方法或轉(zhuǎn)發(fā)消息給一個(gè)另外一個(gè)target辉饱。
Runtime中的class定義如下:

typedef struct objc_class *Class;
typedef struct objec_object {
    Class isa;
} *id;

在Runtime中搬男,我們定義了Objective-C Class的結(jié)構(gòu)體和對象的結(jié)構(gòu)體。在object_object中定義了一個(gè)Class類型的指針isa彭沼,這就是我們通常所說的isa 指針缔逛。在Objective-C Runtime中需要這個(gè)isa指針來檢查一個(gè)對象判斷它的class是什么,并且當(dāng)你給對象發(fā)送消息時(shí)候觀察它是否能夠?qū)x擇器做出反應(yīng)姓惑。最后我們看到id指針褐奴。id指針默認(rèn)除了告訴我們這是一個(gè)Objective-C對象外沒有告訴任何別的消息。通過對象的id指針找到這個(gè)對象的class于毙,查看它是否對某個(gè)方法做出響應(yīng)等敦冬,當(dāng)你指導(dǎo)指針指向的對象是什么的時(shí)候你可以有更多的行為。
IMP(Method Implementations)
typedef id (*IMP)(id self, SEL _cmd, ...);
IMP是編譯器生成的指向方法實(shí)現(xiàn)的函數(shù)指針唯沮。如果你是Objective-C剛?cè)腴T脖旱,你不需要對這些進(jìn)行深入了解,但是這對于你理解Objective-C Runtime是怎樣調(diào)用的方法是有好處的介蛉。
Objective-C的基本實(shí)現(xiàn)是下面這個(gè)樣子:

@interface MyClass :NSObject {
    //vars
    NSInteger counter;
}
//methods
- (void)doFoo;
end

但是在Runtime中則是需要有更多的信息來表示

#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 

從上面可以看到一個(gè)class用有其super class信息萌庆,name, 實(shí)例變量,緩存币旧,協(xié)議等践险。Runtime 通過這信息來響應(yīng)你給類或者類的實(shí)例發(fā)送消息的處理。

Class來定義對象佳恬,那么Class自身也是對象嗎捏境?
是的,Objective-C classes其自身也是對象毁葱。Runtime對于這類對象的處理是通過創(chuàng)建Meta Class(元類)垫言。當(dāng)你發(fā)送[NSObject alloc]你實(shí)際是給類對象發(fā)送一個(gè)消息,并且這個(gè)類對象必須是元類的實(shí)例倾剿。所有元類的父類都是根元類筷频。所有的元類里面都包含其能夠響應(yīng)的類方法的方法列表蚌成。所以當(dāng)你給類對象發(fā)送消息如[NSObject alloc],那么objc_msgSend()在元類中的方法進(jìn)行搜索來查找能夠響應(yīng)這個(gè)操作方法凛捏。
為什么繼承Apple Classes
在開始Cocoa項(xiàng)目的時(shí)候担忧,教程都說通過繼承NSObject類來編碼,并且你也從中體會(huì)到一些益處坯癣。當(dāng)我們實(shí)例化一個(gè)類的對象的時(shí)候
MyObject *object = [[MyObject alloc] init];
首先執(zhí)行的消息是+alloc如果你如果你查看文檔瓶盛,文檔會(huì)這么說:新實(shí)例的isa實(shí)例變量初始化為一個(gè)新的描述類的數(shù)據(jù)結(jié)構(gòu)。所以通過繼承蘋果的類示罗,我們能夠很簡單的分配和創(chuàng)建runtime所期望的結(jié)構(gòu)惩猫。
與Class cache相關(guān)(objc_cache *cache)
當(dāng)Objective-C Runtime通過對象isa指針查詢對象的時(shí)候,能夠找到查看其實(shí)現(xiàn)的很多方法蚜点。然而我們 只是用到這些方法中的一小部分轧房。所以類實(shí)現(xiàn)了一個(gè)cache,當(dāng)你通過類的分發(fā)查找到你要用的方法的時(shí)候?qū)⑵浼尤隿ache绍绘。所以當(dāng)objc_msgSend()從一個(gè)類中查詢一個(gè)selector的時(shí)候奶镶,首先就是查詢cache中是否存在。使用cache的理論依據(jù)是當(dāng)你在class中發(fā)送消息陪拘,你很可能在之后再次發(fā)送消息厂镇,所以把這個(gè)方法加入緩存是有意義的。

MyObject *obj = [[MyObject alloc] init];
 
@implementation MyObject
-(id)init {
    if(self = [super init]){
        [self setVarA:@”blah”];
    }
    return self;
}
@end

上面這段程序執(zhí)行后藻丢,產(chǎn)生了下面幾個(gè)步驟:1.[MyObject alloc]最先執(zhí)行剪撬,MyObject類沒有實(shí)現(xiàn)alloc方法,所以通過superclass指針指向的NSObject中查找該方法悠反。2.查看NSObject是否能夠響應(yīng)+alloc方法残黑,發(fā)現(xiàn)其能夠響應(yīng)。+alloc方法價(jià)差receiver MyObject然后根據(jù)class大小來分配一塊內(nèi)存并初始化一個(gè)isa指針指向MyObject的類斋否。這樣我們就you8leyige實(shí)例梨水,并最后將+alloc方法加到NSObject類的cache中。3.在這之前我們是發(fā)送了一個(gè)類消息茵臭,現(xiàn)在我們講發(fā)送一個(gè)實(shí)例消息-init疫诽。這里我們的class 有- (id)init,因此能夠響應(yīng)這一方法。所以將- (id)init方法加入cache中旦委。4.接下來self = [super init]執(zhí)行奇徒,通過調(diào)用super,于是我們就進(jìn)入NSObject中調(diào)用init方法缨硝。super調(diào)用保證父類中正確初始化其變量摩钙。接下來在subclass中初始化變量。在上面這個(gè)例子中子類的init 并沒有發(fā)生什么重要變化查辩,但是在有些情況下變化還是挺大的胖笛。

#import < Foundation/Foundation.h>
 
@interface MyObject : NSObject
{
 NSString *aString;
}
 
@property(retain) NSString *aString;
 
@end
 
@implementation MyObject
 
-(id)init
{
 if (self = [super init]) {
  [self setAString:nil];
 }
 return self;
}
 
@synthesize aString;
 
@end
 
 
 
int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
 
 id obj1 = [NSMutableArray alloc];
 id obj2 = [[NSMutableArray alloc] init];
  
 id obj3 = [NSArray alloc];
 id obj4 = [[NSArray alloc] initWithObjects:@"Hello",nil];
  
 NSLog(@"obj1 class is %@",NSStringFromClass([obj1 class]));
 NSLog(@"obj2 class is %@",NSStringFromClass([obj2 class]));
  
 NSLog(@"obj3 class is %@",NSStringFromClass([obj3 class]));
 NSLog(@"obj4 class is %@",NSStringFromClass([obj4 class]));
  
 id obj5 = [MyObject alloc];
 id obj6 = [[MyObject alloc] init];
  
 NSLog(@"obj5 class is %@",NSStringFromClass([obj5 class]));
 NSLog(@"obj6 class is %@",NSStringFromClass([obj6 class]));
  
 [pool drain];
    return 0;
}

通常都會(huì)認(rèn)為輸出的結(jié)果為:

NSMutableArray
NSMutableArray 
NSArray
NSArray
MyObject
MyObject

但是實(shí)際上輸出的結(jié)果為:

obj1 class is __NSPlaceholderArray
obj2 class is NSCFArray
obj3 class is __NSPlaceholderArray
obj4 class is NSCFArray
obj5 class is MyObject
obj6 class is MyObject

出現(xiàn)上面這種情況原因就是+alloc方法返回一個(gè)class對象网持,而-init方法返回另一個(gè)class的對象。
那么究竟objc_msgSend發(fā)生了什么呢长踊?
例如有代碼如下
[self printMessageWithString:@"Hello World!"];
編譯器編譯如下:
objc_msgSend(self, @selector(printMessageWithString:), @"Hello World!");
接下來我們通過目標(biāo)對象的isa指針來查找對象(或者它的父類)是否能夠響應(yīng)selector@selector(printMessageWithString:).假設(shè)我們在類分發(fā)表或者cache中找到了這個(gè)選擇器功舀。接下來objc_msgSend()沒有返回值,它開始執(zhí)行并跟隨一個(gè)指向你的方法的指針身弊。接下來你的方法有了返回值辟汰,這看起來好像是objc_msgSend()有了返回值。有關(guān)objc_msgSend()方法阱佛,Bill Bumgarner在其博客的part1,part2中講的很清楚莉擒。總結(jié)起來說就是下面幾點(diǎn):
1.檢查被忽略的選擇器-很顯然瘫絮,如果我們在垃圾回收中運(yùn)行程序,我們忽略-ratian, -release等等填硕。2.檢查空對象麦萤。與其他語言不同,nil在Objective-C中作為參數(shù)傳遞是合法的扁眯。3.查找類中IMP的實(shí)現(xiàn)(方法實(shí)現(xiàn))壮莹。我們首先查找的是與其類,如果找到就根據(jù)指針跳轉(zhuǎn)到相應(yīng)的函數(shù)姻檀。4.如果函數(shù)實(shí)現(xiàn)在cache中沒有查找到命满,就跳轉(zhuǎn)到class的分發(fā)表中查找,若仍然都沒有查找到绣版,跳轉(zhuǎn)步驟5胶台。5.在這一步中首先我們跳轉(zhuǎn)至轉(zhuǎn)發(fā)機(jī)制,在轉(zhuǎn)發(fā)機(jī)制中就意味著你的代碼被編譯器編譯成C函數(shù)杂抽。所以如果你的函數(shù)這么寫:
- (int)doComputeWithNum:(int)aNum
轉(zhuǎn)化為C函數(shù)則是:
int aClass_doComputerWithNum(aClass *self, SEL _cmd, int aNum)
Objective-C Runtime通過喚醒函數(shù)指針來調(diào)用你的方法≌┗#現(xiàn)在可以說你不能直接調(diào)用這個(gè)轉(zhuǎn)換后的方法。然而Cocoa Framework 提供了下面的方法缩麸。

//declare C function pointer
int (computer *)(id, SEL, int);
//methodForSelector is COCOA & not ObjC Runtime
//gets the same function pointer objc_msgSend gets
computeNum = (int (*)(執(zhí)行一個(gè)特定的方法铸磅。這種方法同Objective-C Runtime中`objc_msgSend(id,SEL,int))[target methodForSelector:@selector(doComputeWithNum:)];
 
//execute the C function pointer returned by the runtime
computeNum(obj,@selector(doComputeWithNum:),aNum); 

通過這種方式你可以直接訪問函數(shù)并且在運(yùn)行時(shí)直接喚醒它。甚至可以使用這種方法繞過runtime的動(dòng)態(tài)特性直接)方法如出一轍杭朱。 **Objective-C的消息轉(zhuǎn)發(fā)** 在Objective-C中給對象發(fā)送一個(gè)該對象不知該如何做出反應(yīng)的消息是合法的阅仔。在蘋果的官方文檔中給出的這樣使用的原因之一是為了模擬多重繼承(而Objective-C自身是不支持多重繼承的),或者你希望抽象出來一個(gè)方法但是要在類或者對象中對實(shí)現(xiàn)細(xì)節(jié)進(jìn)行隱藏弧械。Runtime正好能夠滿足這一條件八酒。Runtime是這樣處理的:1.Rumtime在類方法cache和類以及父類的方法分發(fā)表中查找方法,但是沒有查找到特定的要執(zhí)行的方法梦谜。2.于是Objecltive-C Runtime在你的類中提供一個(gè)+ (BOOL)resolveInstanceMehtod:(SEL)aSEL`方法丘跌,這就給了你彌補(bǔ)出錯(cuò)的機(jī)會(huì)袭景,告訴Runtime你通過調(diào)用這個(gè)方法就可以找到要搜索的方法了。舉例如下:

void fooMethdo (id obj, SEL _cmd) {
    NSLog("Doing Foo");
}

你應(yīng)該在通過class_addMethod()方法來解決問題

+ (BOOL)resolveInstanceMehtod:(SEL)aSEL 
{
    if (aSEL == @selector(doFoo:)) {
        class_addMethod([self class], aSEL, (IMP)fooMethod, "v@:");
        return YES;
    }
    return [super resolverInstanceMethod:];
}

上面的class_addMethod()方法中"v@:"是方法的返回值闭树,同時(shí)也作為參數(shù)耸棒,Runtime Guide中編碼類型那部分可以查看具體可以傳入哪些在里面。如果沒有實(shí)現(xiàn)+ (BOOL)resolveInstanceMethod:方法的話报辱,Runtime接下來又提供(id)forwardingTargetForSelector:(SEL)aSelector.這一方法指向Runtime 的另一個(gè)能夠響應(yīng)這個(gè)消息的對象与殃。如果這個(gè)方法仍然沒有做出響應(yīng)的話,Objective-C Runtime就會(huì)執(zhí)行更昂貴的過程來喚醒-(void)forwardInvocation:(NSInvocation *)invocation
看代碼:

- (id)forwardingTargetForSelector:(SEL)aSelector 
{
    if (aSelector == @selector(mysteriousMehtod:)){
        return alternateObject;
    }
    return [super forwardingTargetForSelector:aSelector];
}

從上面的方法返回我們應(yīng)該做到顯示不應(yīng)該返回self碍现,否則就會(huì)陷入無限循環(huán)了幅疼。4.如果上面的方法沒有滿足跳轉(zhuǎn),那么就剩下最后一次彌補(bǔ)的機(jī)會(huì)昼接,就是調(diào)用- (void)forwardInvocation:(NSSInvocation *)anInvocation,如果從沒見過NSInvocation的話爽篷,就記住它本質(zhì)上是對象形式的Objective-C消息。一旦你拿到了NSInvocation慢睡,你就可以改變與這個(gè)消息相關(guān)的任何事物逐工,比如:對象,選擇器漂辐,參數(shù)等等(target, selector, arguments...)泪喊。你可以做的就類似下面這樣的:

- (void)forwardInvocation:(NSinvocation *)invocation 
{
    SEL invSEL = invocation.selector;
    if([altObject resopnseToSelector:invSEL]){
        [invocation invokeWithTarget:alterObject];
    } else {
        [self doesNotRecognizeSelector:invSEL];
    }
}

Non Fragile ivars(Modern Rumtime)
Modern Runtime其中的一個(gè)就是非脆弱的實(shí)例變量(Non Fragile ivars)。當(dāng)編譯器編譯類的時(shí)候髓涯,編譯器生成了一個(gè)實(shí)例變量布局袒啼,在這個(gè)實(shí)例變量不居中描述了訪問你的實(shí)例變量的位置,這就是獲取指向你對象指針的底層細(xì)節(jié)纬纪。在這里實(shí)例變量是通過所占字節(jié)的相對偏移位置來描述的蚓再。但是在Mac OS 10.x以后出現(xiàn)了新的問題,比如 在父類NSObject中定義了實(shí)例變量 NSArray secretAry, NSImage secretImg,這時(shí)一個(gè)子類MyObject繼承自父類育八,又添加了自身的新的實(shí)例變量NSArray students ,NSArray teachers.在實(shí)例變量布局中發(fā)現(xiàn)students和teachers是被覆蓋了对途,因?yàn)橹羔樒D(zhuǎn)位置與父類重疊了。

NSObject NSMyObject:NSObject
0 Class isa 0 Class isa
4 NSArray secretAry 4 NSArray students
8 NSImage secretImg 8 NSArray teachters

于是出現(xiàn)了非脆弱實(shí)例變量髓棋,可以通過下面的表格看出:

NSMyObject:NSObject
0 Class isa
4 NSArray secretAry
8 NSImage secretImg
12 NSArray students
16 NSArray teachers

在非脆弱實(shí)例變量中編譯器將之前父類中的實(shí)例變量在新建一份实檀,這樣runtime在查詢一個(gè)重載自父類的類的時(shí)候會(huì)動(dòng)態(tài)調(diào)整偏移位置。這樣就不會(huì)導(dǎo)致子類中新增的變量被刪除按声。
Objective-C相關(guān)的對象(Objective-C Associated Object)
從Mac OS10.6開始膳犹,Objective-C的Runtime支持動(dòng)態(tài)的給對象添加變量。如果我們想要給已經(jīng)存在的類添加變量签则。以NSView舉例须床,可以這樣做:

#import < Cocoa/Cocoa.h> //Cocoa
#include < objc/runtime.h> //objc runtime api’s
 
@interface NSView (CustomAdditions)
@property(retain) NSImage *customImage;
@end
 
@implementation NSView (CustomAdditions)
 
static char img_key; //has a unique address (identifier)
 
-(NSImage *)customImage
{
    return objc_getAssociatedObject(self,&img_key);
}
 
-(void)setCustomImage:(NSImage *)image
{
    objc_setAssociatedObject(self,&img_key,image,
                             OBJC_ASSOCIATION_RETAIN);
}
 
@end

在runtime.h中可以看到給方法objc_setAssociatedObject()方法傳值是怎么表示的。

/* Associated Object support. */
 
/* objc_setAssociatedObject() options */
enum {
    OBJC_ASSOCIATION_ASSIGN = 0,
    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1,
    OBJC_ASSOCIATION_COPY_NONATOMIC = 3,
    OBJC_ASSOCIATION_RETAIN = 01401,
    OBJC_ASSOCIATION_COPY = 01403
};

混合虛表分發(fā)
你仔細(xì)看最新的runtime代碼渐裂,你會(huì)看到如下介紹:

/***********************************************************************
* vtable dispatch
* 
* Every class gets a vtable pointer. The vtable is an array of IMPs.
* 每個(gè)類都有一個(gè)虛指針表。這個(gè)虛表保存了一個(gè)方法實(shí)現(xiàn)的數(shù)組。
* The selectors represented in the vtable are the same for all classes
*   (i.e. no class has a bigger or smaller vtable).
*   虛方法表中的選擇器對所有類都是一樣的蚣驼。
* Each vtable index has an associated trampoline which dispatches to 
*   the IMP at that index for the receiver class's vtable (after 
*   checking for NULL). Dispatch fixup uses these trampolines instead 
*   of objc_msgSend.
*   每個(gè)虛方法表的索引都與其要分發(fā)到的類的虛方法表所指向的方法之間建立索引当宴。方法的分發(fā)通過索引來實(shí)現(xiàn)拿诸,從而替代了objc_msgSend
* Fragility: The vtable size and list of selectors is chosen at launch 
*   time. No compiler-generated code depends on any particular vtable 
*   configuration, or even the use of vtable dispatch at all.
*   脆弱性:虛方法表的大小以及選擇器表單都是在啟動(dòng)的時(shí)候確定的。由編譯器產(chǎn)生的代碼不會(huì)依賴虛方法表配置,甚至不會(huì)用到虛方法表的分發(fā)。
* Memory size: If a class's vtable is identical to its superclass's 
*   (i.e. the class overrides none of the vtable selectors), then 
*   the class points directly to its superclass's vtable. This means 
*   selectors to be included in the vtable should be chosen so they are 
*   (1) frequently called, but (2) not too frequently overridden. In 
*   particular, -dealloc is a bad choice.
*   內(nèi)存大欣⒐怠:如果一個(gè)類的虛方法表與其父類的虛方法表是一樣的(這個(gè)類沒有覆蓋父類的選擇器)那么這個(gè)類直接指向其父類的虛方法表。這就意味著包含在虛方法表內(nèi)的這些選擇器能夠被選擇鲤遥,這些選擇器通常是1.經(jīng)常被調(diào)用的2.并不經(jīng)常被覆蓋的沐寺。特別要指出的是-dealloc是個(gè)不明智的選擇。
* Forwarding: If a class doesn't implement some vtable selector, that 
*   selector's IMP is set to objc_msgSend in that class's vtable.
* +initialize: Each class keeps the default vtable (which always 
*   redirects to objc_msgSend) until its +initialize is completed.
*   Otherwise, the first message to a class could be a vtable dispatch, 
*   and the vtable trampoline doesn't include +initialize checking.
*   轉(zhuǎn)發(fā):如果一個(gè)類沒有實(shí)現(xiàn)虛方法表中的選擇器那么選擇器的方法實(shí)現(xiàn)就是類虛方法表中的objc_msgSend方法盖奈。
*   初始化:每個(gè)類都持有一個(gè)默認(rèn)的虛方法表(總是指向ojbc_msgSend),直至它初始化完成混坞。反之,發(fā)送給類的第一個(gè)消息會(huì)是一個(gè)虛方法表分發(fā)钢坦,并且這個(gè)分發(fā)并沒有包含初始化檢查拔第。
* Changes: Categories, addMethod, and setImplementation all force vtable 
*   reconstruction for the class and all of its subclasses, if the 
*   vtable selectors are affected.
*   變化:categories,增加方法场钉,設(shè)置函數(shù)實(shí)現(xiàn)都可以強(qiáng)制虛方法表重新創(chuàng)建(這將會(huì)影響到類自身和他的父類)。
**********************************************************************/
The idea behind this is that the runtime is trying to store in this vtable the most called selectors so this in turn speeds up your app because it uses fewer instructions than objc_msgSend. This vtable is the 16 most called selectors which make up an overwheling majority of all the selectors called globally, in fact further down in the code you can see the default selectors for Garbage Collected & non Garbage Collected apps... 
這一思想的背后是為了將那些在虛方法表中調(diào)用次數(shù)最多的選擇器進(jìn)行保存懈涛,這樣加快了你的app響應(yīng)速度逛万,因?yàn)橄啾萶bjc_msgSend方法來說這樣用到更少的指令。下面這個(gè)虛方法表列舉了16個(gè)調(diào)用次數(shù)最多的選擇器組成了一個(gè)全局選擇器批钠。再往下從代碼中你可以看到垃圾收集的默認(rèn)選擇器宇植。
static const char * const defaultVtable[] = {
    "allocWithZone:", 
    "alloc", 
    "class", 
    "self", 
    "isKindOfClass:", 
    "respondsToSelector:", 
    "isFlipped", 
    "length", 
    "objectForKey:", 
    "count", 
    "objectAtIndex:", 
    "isEqualToString:", 
    "isEqual:", 
    "retain", 
    "release", 
    "autorelease", 
};
static const char * const defaultVtableGC[] = {
    "allocWithZone:", 
    "alloc", 
    "class", 
    "self", 
    "isKindOfClass:", 
    "respondsToSelector:", 
    "isFlipped", 
    "length", 
    "objectForKey:", 
    "count", 
    "objectAtIndex:", 
    "isEqualToString:", 
    "isEqual:", 
    "hash", 
    "addObject:", 
    "countByEnumeratingWithState:objects:count:", 
};

現(xiàn)在你知道了這些方法你想做什么呢?在你進(jìn)行棧調(diào)試的時(shí)候埋心,你會(huì)看到上面的一些方法調(diào)用指郁。所有的這些方法作為debug來說,你就將他們理解成objc_msgSend()就好了拷呆。
對于objc_msgSend_fixup方法來說闲坎,當(dāng)你調(diào)用一個(gè)應(yīng)該出現(xiàn)在虛方法表中的方法但是不是常用的16個(gè)方法(objc_msgSend[0-15])中的一個(gè)的時(shí)候,會(huì)觸發(fā)茬斧。比如有時(shí)候你看到objc_msgSend5這就意味著你調(diào)用了虛方法表中常用方法的某一個(gè)腰懂。Runtime會(huì)隨它的意愿指定或者解除。所以你不要通過數(shù)字objc_msgSend10來就認(rèn)為是-lenght.
總結(jié)
總之如果要詳細(xì)的了解Objective-C Runtime项秉,請參考官方文檔Objective-C Runtime Programming GuideObjective-C Runtime Reference

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末绣溜,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子娄蔼,更是在濱河造成了極大的恐慌怖喻,老刑警劉巖底哗,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異锚沸,居然都是意外死亡跋选,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進(jìn)店門咒吐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來野建,“玉大人,你說我怎么就攤上這事恬叹『蛏” “怎么了?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵绽昼,是天一觀的道長唯鸭。 經(jīng)常有香客問我,道長硅确,這世上最難降的妖魔是什么目溉? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮菱农,結(jié)果婚禮上缭付,老公的妹妹穿的比我還像新娘。我一直安慰自己循未,他們只是感情好陷猫,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著的妖,像睡著了一般绣檬。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上嫂粟,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天娇未,我揣著相機(jī)與錄音,去河邊找鬼星虹。 笑死零抬,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的宽涌。 我是一名探鬼主播媚值,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼护糖!你這毒婦竟也來了褥芒?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎锰扶,沒想到半個(gè)月后献酗,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡坷牛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年罕偎,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片京闰。...
    茶點(diǎn)故事閱讀 38,577評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡颜及,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蹂楣,到底是詐尸還是另有隱情俏站,我是刑警寧澤,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布痊土,位于F島的核電站肄扎,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏赁酝。R本人自食惡果不足惜犯祠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望酌呆。 院中可真熱鬧衡载,春花似錦、人聲如沸隙袁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽藤乙。三九已至,卻和暖如春惭墓,著一層夾襖步出監(jiān)牢的瞬間坛梁,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工腊凶, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留划咐,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓钧萍,卻偏偏與公主長得像褐缠,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子风瘦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評論 2 348

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