美文网首页iOS面试
objc_msgSend(一)

objc_msgSend(一)

作者: 小糾丶羯 | 来源:发表于2020-10-08 14:22 被阅读0次

    一、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、导入头文件<objc/message.h>
    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(一)

        本文链接:https://www.haomeiwen.com/subject/kdicpktx.html