objc_msgSend流程分析

Runtime.png

1、Runtime

1.1、Runtime :Objective-C運(yùn)行時(shí),指代碼跑起來(lái)了.被裝載到內(nèi)存中去的過(guò)程,具有動(dòng)態(tài)性,能夠進(jìn)行消息傳遞動(dòng)態(tài)方法解析、消息轉(zhuǎn)發(fā)、類(lèi)型編碼、聲明屬性等一系類(lèi)操作。

1.2、Runtime有兩個(gè)版本:一個(gè)現(xiàn)在(modern)版本,一個(gè)傳統(tǒng)(legacy)版本
傳統(tǒng)版本對(duì)應(yīng)的編程接?:Objective-C 1.0。
現(xiàn)?版本對(duì)應(yīng)的編程接?:Objective-C 2.0 。
傳統(tǒng)版本?于Objective-C 1.0, 32位Mac OS X的平臺(tái)上。
現(xiàn)?版本:iPhone程序Mac OS X v10.5 及以后的系統(tǒng)中的 64 位程序 。

1.3、Runtime交互
Objective-C程序可在三個(gè)不同
的層次上與Runtime進(jìn)行交互:通過(guò)Objective-C源代碼、Foundation框架的NSObject類(lèi)中定義的方法直接調(diào)用運(yùn)行時(shí)函數(shù)

Runtime交互結(jié)構(gòu).png

Runtime交互三種路徑.png

2、objc_msgSend

解釋?zhuān)?strong>發(fā)送一個(gè)帶有簡(jiǎn)單返回值的消息到一個(gè)類(lèi)的實(shí)例

//發(fā)送一個(gè)帶有簡(jiǎn)單返回值的消息到一個(gè)類(lèi)的實(shí)例。
objc_msgSend(id _Nullable self, SEL _Nonnull op, ...)

objc_msgSend:
self-->self指向接收消息的類(lèi)實(shí)例的指針
op-->處理消息的方法的選擇器
...-->方法參數(shù)的變量參數(shù)列表

//發(fā)送一個(gè)帶有簡(jiǎn)單返回值的消息到類(lèi)的實(shí)例的超類(lèi)。
objc_msgSendSuper(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);

objc_msgSendSuper:
super-->指向一個(gè)objc_super數(shù)據(jù)結(jié)構(gòu)的指針。傳遞標(biāo)識(shí) 消息被發(fā)送到的上下文,包括接收消息的類(lèi)的實(shí)例
消息和開(kāi)始搜索方法實(shí)現(xiàn)的超類(lèi)。

op-->SEL類(lèi)型的指針。傳遞將處理消息的方法的選擇器。
...-->方法參數(shù)的變量參數(shù)列表

接下來(lái)通過(guò)源碼來(lái)查看:

  //main函數(shù)中方法的調(diào)用
 #import <Foundation/Foundation.h>
 #import <objc/message.h>

@interface LGTeacher : NSObject
- (void)sayHello;
@end

@implementation LGTeacher
- (void)sayHello{
    NSLog(@"666 %s",__func__);
}
@end

@interface LGPerson : LGTeacher
- (void)sayHello;
- (void)sayNB;
@end

@implementation LGPerson
- (void)sayNB{
    NSLog(@"666");
}
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {

        LGPerson *person = [LGPerson alloc];
        LGTeacher *teacher = [LGTeacher alloc];
        [person sayNB];
        [person sayHello];
    }
    return 0;
}

通過(guò)Clang查看源碼:

__OBJC_RW_DLLIMPORT void objc_msgSend(void);
__OBJC_RW_DLLIMPORT void objc_msgSendSuper(void);
__OBJC_RW_DLLIMPORT void objc_msgSend_stret(void);
__OBJC_RW_DLLIMPORT void objc_msgSendSuper_stret(void);

LGPerson *person = ((LGPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("LGPerson"), sel_registerName("alloc"));
LGTeacher *teacher = ((LGTeacher *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("LGTeacher"), sel_registerName("alloc"));
((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("sayNB"));
((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("sayHello"));

當(dāng)遇到方法調(diào)用時(shí),編譯器生成對(duì)對(duì)應(yīng)
objc_msgSend 、 objc_msgSend_stret, objc_msgSendSuper、 objc_msgSendSuper_stret。
其中
發(fā)送到超類(lèi)的消息使用objc_msgSendSuper
其他消息發(fā)送使用 objc_msgSend
將數(shù)據(jù)結(jié)構(gòu)作為返回值的方法
objc_msgSendSuper_stret
objc_msgSend_stret

在main方法中調(diào)用objc_msgSend ,objc_msgSendSuper,此時(shí)發(fā)現(xiàn)使用clang會(huì)報(bào)錯(cuò),無(wú)法生成main.cpp文件

int main(int argc, const char * argv[]) {
    @autoreleasepool {

        LGPerson *person = [LGPerson alloc];
        LGTeacher *teacher = [LGTeacher alloc];
        [person sayNB];
        [person sayHello];
        //2.消息發(fā)送
     objc_msgSend(person, sel_registerName("sayNB"));
        
       struct objc_super lgsuper;
    lgsuper.receiver = person;//消息的接收者還是person
    lgsuper.super_class = [LGTeacher class];//告訴父類(lèi)是誰(shuí)
     objc_msgSendSuper(&lgsuper, sel_registerName("sayHello"));

    }
    return 0;
}

objc_super結(jié)構(gòu)
```#ifndef OBJC_SUPER
#define OBJC_SUPER

/// Specifies the superclass of an instance. 
struct objc_super {
    /// Specifies an instance of a class.
    __unsafe_unretained _Nonnull id receiver;

    /// Specifies the particular superclass of the instance to message. 
#if !defined(__cplusplus)  &&  !__OBJC2__
    /* For compatibility with old objc-runtime.h header */
    __unsafe_unretained _Nonnull Class class;
#else
    __unsafe_unretained _Nonnull Class super_class;
#endif
    /* super_class is the first class to search */
};
#endif

receiver:消息的接收者
super_class:指定消息的接收者超類(lèi)

輸出打?。?/p>

2021-06-27 16:36:12.851501+0800 001-運(yùn)行時(shí)感受[8467:207092] 666
2021-06-27 16:36:12.851894+0800 001-運(yùn)行時(shí)感受[8467:207092] 666 -[LGTeacher sayHello]
2021-06-27 16:36:12.851951+0800 001-運(yùn)行時(shí)感受[8467:207092] 666
2021-06-27 16:36:12.851992+0800 001-運(yùn)行時(shí)感受[8467:207092] 666 -[LGTeacher sayHello]

總結(jié):
[person sayNB]等價(jià)于objc_msgSend(person, sel_registerName("sayNB"))
無(wú)論是[person sayHello]還是objc_msgSendSuper(&lgsuper, sel_registerName("sayHello"))執(zhí)行的都是父類(lèi)中的實(shí)現(xiàn)

3.objc_msgSend源碼查看

搜索objc_msgSend,打開(kāi)源碼文件objc-msg-arm64.s
_objc_msgSend

對(duì)應(yīng)流程圖.png
    ENTRY _objc_msgSend
    UNWIND _objc_msgSend, NoFrame

    cmp p0, #0          // nil check and tagged pointer check 判斷接受者是否存在
#if SUPPORT_TAGGED_POINTERS //是否支持tagged_pointers對(duì)象
    b.le    LNilOrTagged        //  (MSB tagged pointer looks negative)
#else
    b.eq    LReturnZero    //直接返回空 LReturnZero
#endif
    ldr p13, [x0]       // p13 = isa 
    GetClassFromIsa_p16 p13, 1, x0  // p16 = class  
LGetIsaDone:   //獲取isa完畢LGetIsaDone
    // calls imp or objc_msgSend_uncached
      //開(kāi)啟緩存查找流程
    CacheLookup NORMAL, _objc_msgSend, __objc_msgSend_uncached

LNilOrTagged

#if SUPPORT_TAGGED_POINTERS
LNilOrTagged:  //小對(duì)象或者空判斷
    b.eq    LReturnZero     // nil check 直接返回空了LReturnZero
    GetTaggedClass
    b   LGetIsaDone  //小對(duì)象ISA處理
// SUPPORT_TAGGED_POINTERS
#endif

LReturnZero

LReturnZero:
    // x0 is already zero
    mov x1, #0
    movi    d0, #0
    movi    d1, #0
    movi    d2, #0
    movi    d3, #0
    ret

CacheLookup

開(kāi)啟緩存查找流程.png
.macro CacheLookup Mode, Function, MissLabelDynamic, MissLabelConstant

    mov x15, x16            // stash the original isa
LLookupStart\Function:
    // p1 = SEL, p16 = isa
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16_BIG_ADDRS
    ldr p10, [x16, #CACHE]              // p10 = mask|buckets 
    lsr p11, p10, #48           // p11 = mask
    and p10, p10, #0xffffffffffff   // p10 = buckets
    and w12, w1, w11            // x12 = _cmd & mask
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
    ldr p11, [x16, #CACHE]          // p11 = mask|buckets
#if CONFIG_USE_PREOPT_CACHES
#if __has_feature(ptrauth_calls)
    tbnz    p11, #0, LLookupPreopt\Function
    and p10, p11, #0x0000ffffffffffff   // p10 = buckets
#else
    and p10, p11, #0x0000fffffffffffe   // p10 = buckets
    tbnz    p11, #0, LLookupPreopt\Function
#endif
    eor p12, p1, p1, LSR #7
    and p12, p12, p11, LSR #48      // x12 = (_cmd ^ (_cmd >> 7)) & mask
#else
    and p10, p11, #0x0000ffffffffffff   // p10 = buckets
    and p12, p1, p11, LSR #48       // x12 = _cmd & mask
#endif // CONFIG_USE_PREOPT_CACHES
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
    ldr p11, [x16, #CACHE]              // p11 = mask|buckets
    and p10, p11, #~0xf         // p10 = buckets
    and p11, p11, #0xf          // p11 = maskShift
    mov p12, #0xffff
    lsr p11, p12, p11           // p11 = mask = 0xffff >> p11
    and p12, p1, p11            // x12 = _cmd & mask
#else
#error Unsupported cache mask storage for ARM64.
#endif

    add p13, p10, p12, LSL #(1+PTRSHIFT)
                        // p13 = buckets + ((_cmd & mask) << (1+PTRSHIFT))

                        // do {
1:  ldp p17, p9, [x13], #-BUCKET_SIZE   //     {imp, sel} = *bucket--
    cmp p9, p1              //     if (sel != _cmd) {
    b.ne    3f              //         scan more
                        //     } else {
2:  CacheHit \Mode              // hit:    call or return imp
                        //     }
3:  cbz p9, \MissLabelDynamic       //     if (sel == 0) goto Miss;
    cmp p13, p10            // } while (bucket >= buckets)
    b.hs    1b

    // wrap-around:
    //   p10 = first bucket
    //   p11 = mask (and maybe other bits on LP64)
    //   p12 = _cmd & mask
    //
    // A full cache can happen with CACHE_ALLOW_FULL_UTILIZATION.
    // So stop when we circle back to the first probed bucket
    // rather than when hitting the first bucket again.
    //
    // Note that we might probe the initial bucket twice
    // when the first probed slot is the last entry.


#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16_BIG_ADDRS
    add p13, p10, w11, UXTW #(1+PTRSHIFT)
                        // p13 = buckets + (mask << 1+PTRSHIFT)
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
    add p13, p10, p11, LSR #(48 - (1+PTRSHIFT))
                        // p13 = buckets + (mask << 1+PTRSHIFT)
                        // see comment about maskZeroBits
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
    add p13, p10, p11, LSL #(1+PTRSHIFT)
                        // p13 = buckets + (mask << 1+PTRSHIFT)
#else
#error Unsupported cache mask storage for ARM64.
#endif
    add p12, p10, p12, LSL #(1+PTRSHIFT)
                        // p12 = first probed bucket

                        // do {
4:  ldp p17, p9, [x13], #-BUCKET_SIZE   //     {imp, sel} = *bucket--
    cmp p9, p1              //     if (sel == _cmd)
    b.eq    2b              //         goto hit
    cmp p9, #0              // } while (sel != 0 &&
    ccmp    p13, p12, #0, ne        //     bucket > first_probed)
    b.hi    4b

LLookupEnd\Function:

objc_msgSend總體流程圖:

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

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

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