objc_msgSend(一)

一、Runtime

runtime称之为运行时,与之相对的是编译时
运行时,是代码跑起来,被装载到内存中的过程,是动态阶段,此时出错会导致程序崩溃
编译时,是源代码翻译成机器能识别的代码的过程,是静态阶段,主要做一些词法分析语法分析等操作

二、Runtime调用的三种途径

runtime调用的三种途径

三、objc_msgSend

一、方法的本质
在isa结构分析中,我们通过Clang查看了对象的本质,同样的,这里我们一样可以使用Clang查看方法的本质

//main函数中方法的调用
LGPerson *person = [LGPerson alloc];
[person sayHello];

//clang后的方法的调用
LGPerson *person = ((LGPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("LGPerson"), sel_registerName("alloc"));
((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("sayHello"));

可以看出,方法的本质objc_msgSend(消息发送)
这里我们验证一下,它们是不是真的一样:
1、导入头文件
2、设置enable strict checking of obc_msgSend callsNO(否则objc_msgSend的参数会报错)

LGPerson *person = [LGPerson alloc];
//1.方法的调用
[person sayHello];
//2.消息发送
objc_msgSend(person, sel_registerName("sayHello"));

打印结果如图:

方法调用与objc_msgSend打印结果

可以看到,打印结果是一样的,所以[person sayHello]等价于objc_msgSend(person, sel_registerName("sayHello"))

二、方法的调用,会执行父类的实现

1、定义两个类LGTeacherLGPerson

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

@implementation LGTeacher
- (void)sayHello {
    NSLog(@"666");
}
@end

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

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

可以看到,LGPerson只声明了sayHello方法却没有实现,但是父类LGTeacher实现了

2、通过objc_msgSendSuper调用父类方法

LGPerson *person = [LGPerson alloc];
LGTeacher *teacher = [LGTeacher alloc];
[person sayHello];

struct objc_super lgsuper;
lgsuper.receiver = person;//消息的接收者还是person
lgsuper.super_class = [LGTeacher class];//告诉父类是谁
////消息的接受者还是自己 - 父类 - 请你直接找我的父亲
objc_msgSendSuper(&lgsuper, sel_registerName("sayHello"));

objc_msgSendSuper方法中有两个参数:

OBJC_EXPORT id _Nullable
objc_msgSendSuper(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
#endif

结构体 objc_supersel

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

结构体中需要指定receiversuper_class

3、打印结果如图:

方法的调用,会执行父类的实现

可以看出,无论是[person sayHello]还是objc_msgSendSuper(&lgsuper, sel_registerName("sayHello"))执行的都是父类中的实现

三、初探objc_msgSend

打开源码搜索objc_msgSend,选择arm64.s后缀的文件,可以查找objc_msgSend的源码实现,发现是汇编实现

//---- 消息发送 -- 汇编入口--objc_msgSend主要是拿到接收者的isa信息
ENTRY _objc_msgSend 
//---- 无窗口
    UNWIND _objc_msgSend, NoFrame 
    
//---- p0 和空对比,即判断接收者是否存在,其中p0是objc_msgSend的第一个参数-消息接收者receiver
    cmp p0, #0          // nil check and tagged pointer check 
//---- le小于 --支持taggedpointer(小对象类型)的流程
#if SUPPORT_TAGGED_POINTERS
    b.le    LNilOrTagged        //  (MSB tagged pointer looks negative) 
#else
//---- p0 等于 0 时,直接返回 空
    b.eq    LReturnZero 
#endif 
//---- p0即receiver 肯定存在的流程
//---- 根据对象拿出isa ,即从x0寄存器指向的地址 取出 isa,存入 p13寄存器
    ldr p13, [x0]       // p13 = isa 
//---- 在64位架构下通过 p16 = isa(p13) & ISA_MASK,拿出shiftcls信息,得到class信息
    GetClassFromIsa_p16 p13     // p16 = class 
LGetIsaDone:
    // calls imp or objc_msgSend_uncached 
//---- 如果有isa,走到CacheLookup 即缓存查找流程,也就是所谓的sel-imp快速查找流程
    CacheLookup NORMAL, _objc_msgSend

#if SUPPORT_TAGGED_POINTERS
LNilOrTagged:
//---- 等于空,返回空
    b.eq    LReturnZero     // nil check 

    // tagged
    adrp    x10, _objc_debug_taggedpointer_classes@PAGE
    add x10, x10, _objc_debug_taggedpointer_classes@PAGEOFF
    ubfx    x11, x0, #60, #4
    ldr x16, [x10, x11, LSL #3]
    adrp    x10, _OBJC_CLASS_$___NSUnrecognizedTaggedPointer@PAGE
    add x10, x10, _OBJC_CLASS_$___NSUnrecognizedTaggedPointer@PAGEOFF
    cmp x10, x16
    b.ne    LGetIsaDone

    // ext tagged
    adrp    x10, _objc_debug_taggedpointer_ext_classes@PAGE
    add x10, x10, _objc_debug_taggedpointer_ext_classes@PAGEOFF
    ubfx    x11, x0, #52, #8
    ldr x16, [x10, x11, LSL #3]
    b   LGetIsaDone
// SUPPORT_TAGGED_POINTERS
#endif

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

    END_ENTRY _objc_msgSend

整体执行的流程如图:


objc_msgSend流程图

你可能感兴趣的:(objc_msgSend(一))