OC 底层原理笔记

14 runtime之方法详解

2020-01-20  本文已影响0人  zysmoon
序言
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 *类似
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
SEL sel1 = sel_registerName("test");
SEL sel2 = @selector(test);
NSLog(@"%p %p %p",@selector(test),@selector(test),sel1);

运行结果如下

1653926-2b070a0965d17fd9.png

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

@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指向类的第一个方法地址

@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
#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
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
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
缓存查找

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


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

上一篇 下一篇

猜你喜欢

热点阅读