Runtime和它的消息機(jī)制

Objective-C這門語言,眾所周知,是對C進(jìn)行了擴(kuò)展,具體來說進(jìn)行了兩個(gè)方面的擴(kuò)展,面向?qū)ο蟮奶匦院蛃malltalk中的消息傳遞。而消息傳遞機(jī)制歸根結(jié)底是建立在Runtime庫上。正是這種機(jī)制,決定了Objective-C是一門動態(tài)語言,而同樣是對C擴(kuò)展的C++,是靜態(tài)的。Objective-C將很多決定性的操作依靠Runtime在運(yùn)行時(shí)處理,而C++僅僅在編譯時(shí)就決定了如何處理

消息傳遞

在我們所熟悉的調(diào)用方法背后,最終都是以消息傳遞的方式進(jìn)行處理,例如[object method],從表面上來看,是object調(diào)用了method方法,實(shí)際上,在運(yùn)行時(shí),是給object發(fā)送了一條method消息,這條消息不一定非要object來處理,也可以轉(zhuǎn)發(fā)給其他的對象處理,也可以不進(jìn)行處理,這些種種操作,都是利用Runtime在運(yùn)行時(shí)處理的。
對于[object method]的調(diào)用,編譯器會將其編譯成一行C語言的函數(shù):

objc_msgSend(object, @selector(method));
消息傳遞的步驟

了解了消息傳遞之后,需要進(jìn)一步知道消息傳遞的具體步驟:

  1. 先查看method方法是不是需要被忽略
  2. 查看object對象是否為nil(Objective-C中允許空對象調(diào)用任何方法的原因)
  3. 查看緩存中是否存在方法,系統(tǒng)把近期發(fā)送過的消息記錄在其中,蘋果認(rèn)為這樣可以提高效率
  4. 如果緩存中沒有命中,那么查找該類的方法表,依次從后往前查找
  5. 如果沒有找到,則進(jìn)入父類查找
  6. 如果到了根類還是沒有找到的話,那么就進(jìn)入動態(tài)解析
Runtime中的基本類型

以上過程雖然讀起來蠻容易理解的,但是我們還得搞清楚Runtime是通過什么進(jìn)行上述操作的,這時(shí)候就需要對Runtime的一些基本類型進(jìn)行了解,我們可以在objc/objc.h中看到以下這些定義

struct objc_object {  
    Class isa  OBJC_ISA_AVAILABILITY;
};

struct objc_class {  
    Class isa  OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
    Class super_class;
    const char *name;
    long version;
    long info;
    long instance_size;
    struct objc_ivar_list *ivars;
    **struct objc_method_list **methodLists**;
    **struct objc_cache *cache**;
    struct objc_protocol_list *protocols;
#endif
};

struct objc_method_list {  
    struct objc_method_list *obsolete;
    int method_count;

#ifdef __LP64__
    int space;
#endif

    /* variable length structure */
    struct objc_method method_list[1];
};

struct objc_method {  
    SEL method_name;
    char *method_types;    /* a string representing argument/return types */
    IMP method_imp;
};

不難發(fā)現(xiàn),基本每個(gè)結(jié)構(gòu)都是C語言中的結(jié)構(gòu)體,object_object對應(yīng)著object,object_class對應(yīng)著對象所屬于的類,我們先把目光主要集中在object_class這個(gè)結(jié)構(gòu)體上,可以發(fā)現(xiàn)幾個(gè)上述步驟涉及到的東西:

struct objc_class {  
    Class isa  OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
    Class super_class; // 父類
    const char *name; // 類名
    long version; 
    long info;
    long instance_size;
    struct objc_ivar_list *ivars;
    **struct objc_method_list **methodLists**; // 方法列表
    **struct objc_cache *cache**; // 緩存
    struct objc_protocol_list *protocols;
#endif
};
再次理解消息傳遞的步驟

理解了Runtime的基本結(jié)構(gòu)后,我們再次用專業(yè)的角度來理解消息傳遞:

  1. 查看method方法是否需要被忽略
  2. 查看object對象是否為nil
  3. 通過objc_object中的isa指針,找到該object的objc_class,然后查看objc_cache類型的cache成員中是否有這個(gè)方法,如果有,則找到objc_method中的IMP類型(函數(shù)指針)的成員method_imp去找到實(shí)現(xiàn)內(nèi)容,并執(zhí)行。
  4. 如果沒有找到,則查看objc_method_list類型的成員methodLists中是否有該方法
  5. 如果沒有找到,則通過Class類型的成員super_class找到父類的objc_class,進(jìn)行查找
  6. 要是還沒有找到,則進(jìn)入動態(tài)解析
如果是調(diào)用類方法呢

再次查看這個(gè)objc_class的結(jié)構(gòu):

struct objc_class {  
    Class isa  OBJC_ISA_AVAILABILITY; // metaclass
#if !__OBJC2__
    Class super_class;
    const char *name;
    long version;
    long info;
    long instance_size;
    struct objc_ivar_list *ivars;
    **struct objc_method_list **methodLists**;
    **struct objc_cache *cache**;
    struct objc_protocol_list *protocols;
#endif
};

剛剛第一次看的時(shí)候可能沒有注意到第一個(gè)成員,第一個(gè)成員指向的是結(jié)構(gòu)是metaclass,其中包含靜態(tài)成員變量和靜態(tài)方法(類方法),同時(shí)也包含了一個(gè)isa成員,都指向了父類的metaclass,如果是根類,則指向自己。所以如果是調(diào)用類方法的話,那么就會利用objc_class中的成員isa找到metaclass,然后尋找方法,沒有找到的話則仍然進(jìn)入動態(tài)解析。

動態(tài)解析

通過第二次的理解,對于消息傳遞有了一個(gè)清晰的了解,我們繼續(xù)來研究消息傳遞最后一步的動態(tài)解析。正常我們?nèi)绻{(diào)用了一個(gè)沒有實(shí)現(xiàn)的方法,那么程序會崩潰,并且拋出unrecognized selector to ...的異常,但是利用Runtime,我們可以有三次機(jī)會避免程序崩潰,先通過一張圖來大致了解下過程:

我們具體看下三種方法:

  • resovleInstanceMethod
void otherEat(id self, SEL cmd) {
    NSLog(@"鄭明明");
}

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if ([NSStringFromSelector(sel) isEqualToString:@"eat"]) {
        class_addMethod(self, sel, (IMP)otherEat, "v@");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

以上需要注意幾個(gè)地方:

  1. otherEat函數(shù)是要被class_addMethod作為參數(shù)的,而class_addMethod是Runtime中的API,所以是基于C的,otherEat函數(shù)應(yīng)該是C語言格式的函數(shù)
  2. class_addMethod方法可謂是核心,那么依次來看他的參數(shù)的含義:
    • first:添加到哪個(gè)類
    • second:添加哪個(gè)SEL選擇器
    • third:IMP函數(shù)指針
    • fourth:IMP指針指向的函數(shù)返回值和參數(shù)類型
      • v@:代表返回值是void類型,無參數(shù)
      • i@:代表返回值是int類型,無參數(shù)
      • v@:i@:代表返回值是void類型,參數(shù)是int類型,存在一個(gè)參數(shù)(多參數(shù)依次累加)
  3. 如果沒有調(diào)用class_addMethod成功添加方法,那么就會到下一個(gè)方法
  • forwardingTargetForSelector
- (id)forwardingTargetForSelector:(SEL)aSelector {
//    return [[Woman alloc]init];
    return nil;
}

如果返回了另外一個(gè)對象,那么動態(tài)解析又會重新以另外一個(gè)對象為接受者執(zhí)行,如果返回nil,則又繼續(xù)進(jìn)入到下一個(gè)方法

  • methodSignatureForSelector + forwardInvocation
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    if ([NSStringFromSelector(aSelector) isEqualToString:@"eat"]) {
        return [NSMethodSignature signatureWithObjCTypes:"@v"];
    }
    return [super methodSignatureForSelector:aSelector];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    // 改變消息接受對象
    /*
    Woman *temp = [[Woman alloc]init];
    [anInvocation invokeWithTarget:temp];
     */
    
    // 改變執(zhí)行的消息
    [anInvocation setSelector:@selector(otherEat)];
    [anInvocation invokeWithTarget:self];
}

methodSignatureForSelector方法返回一個(gè)返回值以及參數(shù)的封裝值,然后會進(jìn)入到下一個(gè)方法,forwardInvocation,這個(gè)方法的功能可以說是前兩個(gè)方法的結(jié)合,通過操作NSInvocation對象,既可以改變需執(zhí)行的消息,又可以改變消息的接受對象

總結(jié)

Runtime為Objective-C提供了很多可能,了解消息機(jī)制,更加有助于對Objective-C這門語言特性的掌握

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,041評論 0 9
  • 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的...
    西木閱讀 30,881評論 33 466
  • 我們常常會聽說 Objective-C 是一門動態(tài)語言,那么這個(gè)「動態(tài)」表現(xiàn)在哪呢?我想最主要的表現(xiàn)就是 Obje...
    Ethan_Struggle閱讀 2,326評論 0 7
  • 轉(zhuǎn)載:http://yulingtianxia.com/blog/2014/11/05/objective-c-r...
    F麥子閱讀 828評論 0 2
  • 本文詳細(xì)整理了 Cocoa 的 Runtime 系統(tǒng)的知識,它使得 Objective-C 如虎添翼,具備了靈活的...
    lylaut閱讀 864評論 0 4

友情鏈接更多精彩內(nèi)容