iOS方法缓存-散列表
Class 内部结构中有个方法缓存( cache_t ), 用散列表来缓存曾经调用过的方法,可以提高方法的查找速度
每个类都有一个自己的方法列表数组,每次调用方法的时候,都会去找当前的类的方法数组看看有没有这个方法,如果没找到,就去父类寻找,不过,在这些之前,最先去缓存数组cache 里面找,如果是第一次调用,在方法列表里面找到这个方法之后,会把这个方法在父类和当前累的缓存数组里面各保存· 一份
当数组已经装不下的时候,数组就会翻倍扩容,然后把之前的数据清空
怎么存储缓存的方法:散列表技术 key-value
cache_t 的数据结构
思路 :调用方法的时候,通过方法名(key)去到通过isa指针找到的对应的类的缓存数组里面的散列表( struct _bucket_t *_buckets )查找,如果找到,直接拿出对应的函数内存地址。直接调用(这就是为啥一个类里面不能有相同名称的方法)
#import <objc/runtime.h>
#import <objc/message.h>
NSLog(@"%p",@selector(test)); // 输出方法的内存地址
objc_msgSend(person, @selector(test)); // 调用person类的test方法
散列表的数据储存位置:
@selector(personTest) @ _mask( 散列表的长度-1 ) = 这个数据缓存的位置的下标,也就是缓存方法的索引,这个下标经过位运算之后,一定会小于或者等于散列表的长度-1 ,就不会出现数组越界的情况了
散列表数据结构
打印缓存的方法
Student *student = [[Student alloc]init];
mj_objc_class *studentClass = (__bridge mj_objc_class *)[Student class];
[student studentTest];
[student personTest];
[student studentTest];
[student personTest];
cache_t cache = studentClass->cache; // 拿到缓存cache
bucket_t *buckets = cache._buckets; // 里面是通过散列表的思路进行缓存的
// mj的获取方法
// NSLog(@"%s %p",@selector(studentTest), cache.imp(@selector(studentTest)));
// bucket_t bucket = buckets[(long long)@selector(personTest) & cache._mask ];
// NSLog(@"------------");
// NSLog(@"%s %p",bucket._key, bucket._imp);
// NSLog(@"------------");
// 打印全部缓存数据
for (int i = 0 ; i <= cache._mask ; i++) {
bucket_t bucket = buckets[i];
NSLog(@"%s %p",bucket._key, bucket._imp);
}
// 直接通过 索引去除对应的bucket
// 1、 算出索引 @selector(personTest) & _mask
bucket_t bucket = buckets[(long long)@selector(studentTest) & cache._mask ];
NSLog(@"%s %p",bucket._key, bucket._imp);
最后:
感谢 MJ 老师的****,受益良多,有兴趣的同学可以了解一下,MJ 底层原理开发,腾讯视频就有,为了避免说我打广告,链接我就不发了