美文网首页OC 底层原理笔记
14 runtime之方法详解

14 runtime之方法详解

作者: zysmoon | 来源:发表于2020-01-20 14:41 被阅读0次
    序言
    • Objective-C是一门动态性比较强的编程语言,跟C、C++等语言有着很大的不同
    • Objective-C的动态性是由Runtime API来支撑的
    • Runtime API提供的接口基本都是C语言的,源码由C\C++\汇编语言编写
    Class的结构
    1653926-a9d2c08107ca7e07.png
    class_rw_t

    class_rw_t里面的methods、properties、protocols是二维数组,是可读可写的,包含了类的初始内容、分类的内容

    1653926-2c65919e7929993f.png
    class_ro_t

    class_ro_t里面的baseMethodList、baseProtocols、ivars、baseProperties是一维数组,是只读的,包含了类的初始内容

    1653926-2ad37ce629569ea0.png
    method_t

    method_t是对方法\函数的封装

    struct method_t {
      SEL name;  // 函数名
      const char *types;  // 编码 (返回值类型,参数类型)
      IMP imp;  // 指向函数的指针(函数地址)
    };
    
    
    1. SEL代表方法\函数名,一般叫做选择器,底层结构跟char *类似
    • 可以通过@selector()sel_registerName()获得
    • 可以通过sel_getName()NSStringFromSelector()转成字符串
    • 不同类中相同名字的方法,所对应的方法选择器是相同的
    typedef struct objc_selector *SEL;
    
    
    2. types包含了函数返回值、参数编码的字符串
    1653926-e961be6347af5005.png
    3. IMP 代表函数的具体实现
    1653926-180683a0b61e9f98.png
    4. Type Encoding

    iOS中提供了一个叫做@encode的指令,可以将具体的类型表示成字符串编码

    1653926-89bd76991dd323fd.png
    • 1.SEL代码例子如下
    // 1.SEL
    SEL sel1 = sel_registerName("test");
    SEL sel2 = @selector(test);
    NSLog(@"%p %p %p",@selector(test),@selector(test),sel1);
    
    

    运行结果如下

    1653926-2b070a0965d17fd9.png

    SEL只是一个方法字符串,和属于哪个类,或者通过runtime获取都没有关系,他们的内存地址都是一样的

    • 2.IMP代码例子如下
    @interface Person : NSObject
    - (void)test;
    @end
    
    @implementation Person
    - (void)test {
        NSLog(@"%s",__func__);
    }
    @end
    
    #import <Foundation/Foundation.h>
    #import <objc/runtime.h>
    #import "Person.h"
    #import "MJClassInfo.h"
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            Person *person = [[Person alloc] init];
    
            mj_objc_class *cls = (__bridge mj_objc_class *)[Person class];
            class_rw_t *data = cls->data();
    
            // 2.IMP
            [person test];
    
            NSLog(@"end");
        }
        return 0;
    }
    
    
    1653926-f041ec58047ba057.png 1653926-28c072c64d09731f.png

    可以知道IMP指向类的第一个方法地址

    • 3.types代码例子如下
    @interface Person : NSObject
    - (void)test;
    // "i24@0:8i16f20"
    // 0id 8SEL 16int 20float  == 24
    - (int)test:(int)age height:(float)height;
    @end
    
    @implementation Person
    - (void)test {
        NSLog(@"%s",__func__);
    }
    - (int)test:(int)age height:(float)height {
        return age + height;
    }
    @end
    
    #import <Foundation/Foundation.h>
    #import <objc/runtime.h>
    #import "Person.h"
    #import "MJClassInfo.h"
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            Person *person = [[Person alloc] init];
    
            mj_objc_class *cls = (__bridge mj_objc_class *)[Person class];
            class_rw_t *data = cls->data();
    
            // 3.types
            [person test:100 height:100];
    
            NSLog(@"end");
        }
        return 0;
    }
    
    

    运行结果如下

    1653926-8e4b41dd06547b81.png

    "i24@0:8i16f20"
    0id 8SEL 16int 20float == 24

    • (int)test:(int)age height:(float)height;
    三 方法缓存

    Class内部结构中有个方法缓存(cache_t),用散列表(哈希表)来缓存曾经调用过的方法,可以提高方法的查找速度

    1653926-f1cf787a6215381c.png 1653926-4685a77051e80e34.png
    • _mask的长度代码例子佐证
      分别建三个类并且有继承关系GoodStudent -> Student -> Person,并且内部都有一个打印方法,详情见项目连接地址
    #import <Foundation/Foundation.h>
    #import "Person.h"
    #import "Student.h"
    #import "GoodStudent.h"
    #import "MJClassInfo.h"
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            GoodStudent *goodStudent = [[GoodStudent alloc] init];
            mj_objc_class *goodStudentClass = (__bridge mj_objc_class *)[GoodStudent class];
    
            [goodStudent goodStudentTest];
            [goodStudent studentTest];
            [goodStudent personTest];
    
             NSLog(@"--------------------------");
        }
        return 0;
    }
    
    

    打印结果

    1653926-6fedff16394217ff.png 1653926-e32199903c60b2e0.png 1653926-8c9a0c8ce17194c8.png 1653926-c35f942dc499ac6b.png
    • 2.bucket的佐证
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            GoodStudent *goodStudent = [[GoodStudent alloc] init];
            mj_objc_class *goodStudentClass = (__bridge mj_objc_class *)[GoodStudent class];
    
            // 1\. _mask的数量
            [goodStudent goodStudentTest];
            [goodStudent studentTest];
            [goodStudent personTest];
            [goodStudent goodStudentTest];
            [goodStudent studentTest];
            [goodStudent personTest];
            NSLog(@"--------------------------");
    
            // 2.打印bucket_t的值
            cache_t cache = goodStudentClass->cache;
            bucket_t *buckets = cache._buckets;
            for (int i = 0; i <= cache._mask; i++) {
                bucket_t bucket = buckets[I];
                NSLog(@"%s %p", bucket._key, bucket._imp);
            }
        }
        return 0;
    }
    
    

    打印结果

    1653926-959539bacfd3fa6d.png 1653926-aa4da3fcbd5b459a.png
    • 3.cache.imp通过哈希表找对应方法佐证
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            GoodStudent *goodStudent = [[GoodStudent alloc] init];
            mj_objc_class *goodStudentClass = (__bridge mj_objc_class *)[GoodStudent class];
    
            // 1\. _mask的数量
            [goodStudent goodStudentTest];
            [goodStudent studentTest];
            [goodStudent personTest];
            [goodStudent goodStudentTest];
            [goodStudent studentTest];
            [goodStudent personTest];
            NSLog(@"--------------------------");
    
            // 3.通过cache.imp找到方法
            cache_t cache = goodStudentClass->cache;
            NSLog(@"%s %p", @selector(personTest), cache.imp(@selector(personTest)));
            NSLog(@"%s %p", @selector(studentTest), cache.imp(@selector(studentTest)));
            NSLog(@"%s %p", @selector(goodStudentTest), cache.imp(@selector(goodStudentTest)));
    
            NSLog(@"end");
        }
        return 0;
    }
    
    
    1653926-cf2168f44da45189.png
    缓存查找
    • objc-cache.mm
    • bucket_t * cache_t::find(cache_key_t k, id receiver)

    本文参考:
    路飞_Luck (https://www.jianshu.com/p/07f7b96bb03f)
    以及借鉴MJ的教程视频
    非常感谢.


    项目连接地址
    runtime-method详解
    runtime-cache

    相关文章

      网友评论

        本文标题:14 runtime之方法详解

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