objc_msgSend流程分析

Runtime.png

1、Runtime

1.1、Runtime :Objective-C运行时,指代码跑起来了.被装载到内存中去的过程,具有动态性,能够进行消息传递动态方法解析消息转发类型编码声明属性等一系类操作。

1.2、Runtime有两个版本:一个现在(modern)版本,一个传统(legacy)版本
传统版本对应的编程接⼝:Objective-C 1.0。
现⾏版本对应的编程接⼝:Objective-C 2.0 。
传统版本⽤于Objective-C 1.0, 32位Mac OS X的平台上。
现⾏版本:iPhone程序Mac OS X v10.5 及以后的系统中的 64 位程序 。

1.3、Runtime交互
Objective-C程序可在三个不同
的层次上与Runtime进行交互:通过Objective-C源代码Foundation框架的NSObject类中定义的方法直接调用运行时函数

Runtime交互结构.png

Runtime交互三种路径.png

2、objc_msgSend

解释:发送一个带有简单返回值的消息到一个类的实例

//发送一个带有简单返回值的消息到一个类的实例。
objc_msgSend(id _Nullable self, SEL _Nonnull op, ...)

objc_msgSend:
self-->self指向接收消息的类实例的指针
op-->处理消息的方法的选择器
...-->方法参数的变量参数列表

//发送一个带有简单返回值的消息到类的实例的超类。
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-->指向一个objc_super数据结构的指针。传递标识 消息被发送到的上下文,包括接收消息的类的实例
消息和开始搜索方法实现的超类。

op-->SEL类型的指针。传递将处理消息的方法的选择器。
...-->方法参数的变量参数列表

接下来通过源码来查看:

  //main函数中方法的调用
 #import 
 #import 

@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;
}

通过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"));

当遇到方法调用时,编译器生成对对应
objc_msgSend 、 objc_msgSend_stret, objc_msgSendSuper、 objc_msgSendSuper_stret。
其中
发送到超类的消息使用objc_msgSendSuper
其他消息发送使用 objc_msgSend
将数据结构作为返回值的方法
objc_msgSendSuper_stret
objc_msgSend_stret

在main方法中调用objc_msgSend ,objc_msgSendSuper,此时发现使用clang会报错,无法生成main.cpp文件

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

        LGPerson *person = [LGPerson alloc];
        LGTeacher *teacher = [LGTeacher alloc];
        [person sayNB];
        [person sayHello];
        //2.消息发送
     objc_msgSend(person, sel_registerName("sayNB"));
        
       struct objc_super lgsuper;
    lgsuper.receiver = person;//消息的接收者还是person
    lgsuper.super_class = [LGTeacher class];//告诉父类是谁
     objc_msgSendSuper(&lgsuper, sel_registerName("sayHello"));

    }
    return 0;
}

objc_super结构
```#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:指定消息的接收者超类

输出打印:

2021-06-27 16:36:12.851501+0800 001-运行时感受[8467:207092] 666
2021-06-27 16:36:12.851894+0800 001-运行时感受[8467:207092] 666 -[LGTeacher sayHello]
2021-06-27 16:36:12.851951+0800 001-运行时感受[8467:207092] 666
2021-06-27 16:36:12.851992+0800 001-运行时感受[8467:207092] 666 -[LGTeacher sayHello]

总结:
[person sayNB]等价于objc_msgSend(person, sel_registerName("sayNB"))
无论是[person sayHello]还是objc_msgSendSuper(&lgsuper, sel_registerName("sayHello"))执行的都是父类中的实现

3.objc_msgSend源码查看

搜索objc_msgSend,打开源码文件objc-msg-arm64.s
_objc_msgSend

对应流程图.png
    ENTRY _objc_msgSend
    UNWIND _objc_msgSend, NoFrame

    cmp p0, #0          // nil check and tagged pointer check 判断接受者是否存在
#if SUPPORT_TAGGED_POINTERS //是否支持tagged_pointers对象
    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
      //开启缓存查找流程
    CacheLookup NORMAL, _objc_msgSend, __objc_msgSend_uncached

LNilOrTagged

#if SUPPORT_TAGGED_POINTERS
LNilOrTagged:  //小对象或者空判断
    b.eq    LReturnZero     // nil check 直接返回空了LReturnZero
    GetTaggedClass
    b   LGetIsaDone  //小对象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

开启缓存查找流程.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

你可能感兴趣的:(objc_msgSend流程分析)