runtime01-消息機制

OC及其動態(tài)性

Objective-C是基于C封裝的一門面向?qū)ο蟮恼Z言,底層實現(xiàn)是通過C/C++代碼實現(xiàn)的。OC語言最大的特點就是其動態(tài)性,它會盡可能地把決策從編譯時和連接時推遲到運行時(簡單來說,就是編譯后的文件不全是機器指令,還有一部分中間代碼,在運行的時候,通過Runtime再把需要轉(zhuǎn)換的中間代碼再翻譯成機器指令)。

Runtime與消息機制

Runtime是OC的一套由C和匯編編寫的庫(一些調(diào)用頻率較高的方法是由匯編編寫的),它是OC具有動態(tài)的的最主要條件。當程序執(zhí)行[object doSomething]時,會向消息接收者(object)發(fā)送一條消息(doSomething),runtime會根據(jù)消息接收者是否能響應(yīng)該消息而做出不同的反應(yīng)。因此OC方法在運行時,都是作為消息在傳遞的,我們甚至可以把方法叫做消息,甚至可以說OC就是一門消息語言。

OC對象

在我們講消息機制前首先要了解OC的對象,才能了解對象的方法調(diào)用過程。

OC的的底層其實就結(jié)構(gòu)體,長這個樣子。

OC對象.png

這個就是結(jié)構(gòu)體的內(nèi)部;
由上而下,objc_class這個結(jié)構(gòu)體就是一個對象,它由三部分組成,

  • isa (指向?qū)ο蟾割惖闹羔?
  • superclass(它的父類)
  • cache_t(對象的調(diào)用過的方法列表)
  • bits(對象的更多信息)

class_rw_t是將bit通過位運算的結(jié)果取其[3, 47]位,轉(zhuǎn)換而成。這里包含了類的方法方法列表,屬性列表及協(xié)議列表等。ro就是rootclass的意思他其中包含了類的成員變量等。

OC類的繼承體系

NSString *str = [NSString string]

str是一個事例對象,它內(nèi)部的isa指針指向它的類NSString,
NSString也是一個OC類,它內(nèi)部也有isa,isa指向它的元類對象NSString meta-class (NSString的元類對象是NSString)
NSString meta-class````也是個OC類,它的isa指向它的元類meta-class。meta-class也是一個對象,它的isa指向哪里?為了防止它無限延伸下去,設(shè)計出了meta-class指向基類的meta-class以此作為它們的所屬類。即,任何NSObject繼承體系下的meta-class都使用NSObjectmeta-class```作為自己的所屬類,而基類的meta-class的isa指針是指向它自己。這樣就形成了一個完美的閉環(huán)。
事例如下圖。

實例對象、類、元類關(guān)系

繼承體系

消息機制

已經(jīng)介紹類OC對象,現(xiàn)在可以runtime消息機制的主題了。我們的調(diào)用類方法也好,對象方法也好,都會被轉(zhuǎn)成 objc_msgSend的消息。由于OC的底層是由C和C++實現(xiàn)的,我們就在OC文件目錄下把它轉(zhuǎn)成C++文件。

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc +要轉(zhuǎn)的OC文件
例子:xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m

 //定義一個Dog類,它有兩個方法一個是eat對象方法,另一個是eat類方法。
        Dog *wangCai = [[Dog alloc]init];
        [wangCai eat];
        [Dog eat];
        
//        (objc_msgSend)(wangCai, sel_registerName("eat"));  //對象方法
//        (objc_msgSend)(objc_getClass("Dog"), sel_registerName("eat"));  //類方法

這是的Dog類的.h文件

@interface Dog : NSObject
- (void)eat;
- (void)bark;
+ (void)play;
@end

我們注釋掉它的eat方法實現(xiàn),

#import "Dog.h"
#import <objc/runtime.h>
#import "Cat.h"

@implementation Dog

//- (void)eat{
//    NSLog(@"dog--eat");
//}
- (void)bark{
    NSLog(@"dog--bark");
}
+ (void)play{
    NSLog(@"classfunc-dog--play");
}
@end
消息發(fā)送

消息機制--動態(tài)方法解析

現(xiàn)在調(diào)用eat方法它會出錯,其實OC在找不到方法實現(xiàn)的時候,它會動態(tài)調(diào)用runtime的這個方法+ (BOOL)resolveInstanceMethod:(SEL)sel

#import "Dog.h"
#import <objc/runtime.h>
#import "Cat.h"

@implementation Dog

//- (void)eat
//{
//    NSLog(@"dog--eat");
//}


- (void)bark{
    NSLog(@"dog--bark");
}

+ (void)play{
    NSLog(@"classfunc-dog--play");
}

/*
 2.0動態(tài)方法解析
 */
+ (BOOL)resolveInstanceMethod:(SEL)sel{
    if (sel == @selector(eat)) {
        // 獲取其他方法
        Method method = class_getInstanceMethod(self, @selector(bark)); //調(diào)用Dog類的bark方法, 打印輸出的結(jié)果是dog--bark

        // 動態(tài)添加test方法的實現(xiàn)
        class_addMethod(self, sel,
                        method_getImplementation(method),
                        method_getTypeEncoding(method));

        // 返回YES代表有動態(tài)添加方法
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

這樣我們就實現(xiàn)了動態(tài)給OC對象尋找實現(xiàn),防止崩潰的方法


動態(tài)方法解析

消息機制--消息轉(zhuǎn)發(fā)

如果我們不實現(xiàn)resolveInstanceMethod程序必然會崩潰嗎?別急runtime還有第二個機制防止奔潰- (id)forwardingTargetForSelector:(SEL)aSelector消息轉(zhuǎn)發(fā)機制,你不是處理不了嗎?那你吧消息轉(zhuǎn)給別人,讓有能力的類處理。
我們定義一個處理這eat方法的Cat類,.h的聲明寫不寫都成,因為它會直接在方法實現(xiàn)中搜取

#import "Cat.h"

@implementation Cat

- (void)eat{
    NSLog(@"Cat--eat");
}
@end

我們在Dog的類中需要做如下處理,把消息轉(zhuǎn)發(fā)給Cat讓Cat幫它去處理

///Dog.m類
/*
 3.0 消息轉(zhuǎn)發(fā)
 */
- (id)forwardingTargetForSelector:(SEL)aSelector
{
    if (aSelector == @selector(eat)) {
        return [[Cat alloc] init]; //返回空否
    }
    
    return [super forwardingTargetForSelector:aSelector];
}

//這樣處理Cat的eat類會被調(diào)用,打印出Cat--eat

當然消息轉(zhuǎn)發(fā)的時候也不知道轉(zhuǎn)給誰(即- (id)forwardingTargetForSelector:(SEL)aSelector返回的是空對象nil),可以在- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector 方法里自己生成個簽名,然后實現(xiàn)
- (void)forwardInvocation:(NSInvocation *)anInvocation方法,收集日志防止程序崩潰

///Dog.m類
/*
 3.0 消息轉(zhuǎn)發(fā)
 */
- (id)forwardingTargetForSelector:(SEL)aSelector{
    return nil;
}

/*
 3.1消息轉(zhuǎn)發(fā)
 */
// 方法簽名:返回值類型、參數(shù)類型
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
    if (aSelector == @selector(eat)) {
        return [NSMethodSignature signatureWithObjCTypes:"@@:*"];//手動創(chuàng)建一個方法簽名
    }

    return [super methodSignatureForSelector:aSelector];
}

/*
 3.1.1消息轉(zhuǎn)發(fā)
 */
//自定義的方法
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    NSLog(@"調(diào)用的方法找不到實現(xiàn)");
}

如果你這會兒知道誰能處理這個消息,也可以這樣處理,

#import "Dog.h"
#import <objc/runtime.h>
#import "Cat.h"

@interface Dog ()
@property (nonatomic,strong) Cat *miCat;
@end

@implementation Dog

//- (void)eat{
//    NSLog(@"dog--eat");
//}
- (void)bark{
    NSLog(@"dog--bark");
}
+ (void)play{
    NSLog(@"classfunc-dog--play");
}
/*
 3.0 消息轉(zhuǎn)發(fā)
 */
- (id)forwardingTargetForSelector:(SEL)aSelector{
    return nil;
}

/*
 3.1消息轉(zhuǎn)發(fā)
 */
// 方法簽名:返回值類型、參數(shù)類型
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    if (aSelector == @selector(eat)) {
        self.miCat = [Cat new];
        return [self.miCat methodSignatureForSelector:aSelector];
    }
    return [super methodSignatureForSelector:aSelector];
}

/*
 3.1.1消息轉(zhuǎn)發(fā)
 */
//自定義的方法
- (void)forwardInvocation:(NSInvocation *)anInvocation{
//    NSLog(@"調(diào)用的方法找不到實現(xiàn)");
    if (anInvocation.selector == @selector(eat)) {
        [anInvocation invokeWithTarget:self.miCat];
    }
}
@end

/// Cat類的eat方法同樣會被調(diào)用,打印出Cat--eat
消息轉(zhuǎn)發(fā)

消息鏈總計起來如下:

  • 1.查找

1.本類查找方法,若有響應(yīng),若無去父類查找;

  1. 父類查找,若有響應(yīng),如無去父類查找,直至元類;
  2. 元類有響應(yīng),元類無,走消息分發(fā)機制
  • 2.消息轉(zhuǎn)發(fā)

1.消息重新交給被掉用類,被掉用類可以讓自己別的方法替代響應(yīng)

  • 3.動態(tài)方法解析
  1. 被掉用類將方法拋給指定的類, 讓它響應(yīng)該方法
  • 4.消息轉(zhuǎn)發(fā)

1.方法重新回到被掉用類自身,被掉用類,手動生成方法簽名

  1. 將改消息交給指定的類,讓它體自己響應(yīng)

方法查找不到時,只有(2、3、4)三層保護全沒有處理才會報錯。

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

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,030評論 0 9
  • 參考鏈接: http://www.cnblogs.com/ioshe/p/5489086.html 簡介 Runt...
    樂樂的簡書閱讀 2,240評論 0 9
  • 我們常常會聽說 Objective-C 是一門動態(tài)語言,那么這個「動態(tài)」表現(xiàn)在哪呢?我想最主要的表現(xiàn)就是 Obje...
    Ethan_Struggle閱讀 2,319評論 0 7
  • 一、Runtime簡介 Runtime簡稱運行時。OC就是運行時機制,也就是在運行時候的一些機制,其中最主要的是消...
    林安530閱讀 1,112評論 0 2
  • 轉(zhuǎn)載:http://yulingtianxia.com/blog/2014/11/05/objective-c-r...
    F麥子閱讀 827評論 0 2

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