Runtime源码理解cache_t(方法缓存)
2021-09-27 本文已影响0人
风雨彩虹_123
Class内部结构中有个方法缓存(catch_t),用散列表来缓存曾经调用过的方法,可以提高方法的查找速度。
struct objc_class : objc_object {
Class isa;
Class superclass;
cache_t cache; // 方法缓存
class_data_bits_t bits; // 使用共用体存储类的信息 &FAST_DATA_MASK 获得class_rw_t结构
}
class_ro_t和class_rw_t的区别
class_ro_t存储了当前类在编译期就已经确定的属性、方法以及遵循的协议,里面是没有分类的方法的,是只读的不能修改。
struct class_ro_t {
method_list_t * baseMethodList; //方法列表(一维数组)
protocol_list_t * baseProtocols;//协议列表(一维数组)
const ivar_list_t * ivars; //成员变量列表
property_list_t *baseProperties; // 属性列表(一维数组)
};
class_rw_t是在runtime时才确定,它会先将class_ro_t的内容拷贝过去,然后再将当前类的分类的这些属性、方法等拷贝到其,所以可以说class_rw_t是class_ro_t的超集。
struct class_rw_t {
//指向只读的结构体,存放类初始信息
const class_ro_t *ro;
/*
这三个都是二维数组,是可读可写的,包含了类的初始信息、分类的信息
methods数组中存储着method_list_t 数组
method_list_t数组 中存储着method_t
runtime会将class_ro_t 中的类初始信息合并到这三个数组中
*/
method_array_t methods; //方法列表(二维数组)
property_array_t properties;;//属性列表(二维数组)
protocol_array_t protocols;// 协议列表(二维数组)
}
method_t是对方法/函数的封装。
struct method_t {
SEL name; //函数名 不同类中相同名字的方法,所对应的方法选择器是相同的。
const char *types; //编码(包括返回值类型,参数类型)
IMP imp; //指向函数的指针
};
cache_t 方法缓存
struct cache_t {
struct bucket_t *_buckets; //散列表
mask_t _mask; //散列表的长度 - 1 作用:和SEL进行&操作,得到散列表的下标,存储方法
mask_t _occupied; //已经缓存的方法数量 作用: 用于cache_t扩容的判断
}
struct bucket_t {
cache_key_t _key; //SEL做为key
IMP _imp; //函数的内存地址
}
查看缓存
编写Object的底层C++结构体代码
#import <Foundation/Foundation.h>
#ifndef ClassInfo_h
#define ClassInfo_h
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL //用于取出isa所表示class 、meta_class 的地址
# elif __x86_64__ //模拟器或Mac电脑
# define ISA_MASK 0x00007ffffffffff8ULL
# endif
#if __LP64__
typedef uint32_t mask_t;
#else
typedef uint16_t mask_t;
#endif
typedef uintptr_t cache_key_t;
#if __arm__ || __x86_64__ || __i386__
#define CACHE_END_MARKER 1
static inline mask_t cache_next(mask_t i, mask_t mask) {
return (i+1) & mask;
}
#elif __arm64__
#define CACHE_END_MARKER 0
static inline mask_t cache_next(mask_t i, mask_t mask) {
return i ? i-1 : mask;
}
#else
#error unknown architecture
#endif
//散列表中缓存的方法信息
struct bucket_t {
cache_key_t _key;
IMP _imp;
};
//缓存方法的底层结构
struct cache_t {
bucket_t *_buckets;
mask_t _mask;
mask_t _occupied;
IMP imp(SEL selector)
{
mask_t begin = _mask & (long long)selector;
mask_t i = begin;
do {
if (_buckets[i]._key == 0 || _buckets[i]._key == (long long)selector) {
return _buckets[i]._imp;
}
} while ((i = cache_next(i, _mask)) != begin);
return NULL;
}
};
struct entsize_list_tt {
uint32_t entsizeAndFlags;
uint32_t count;
};
// 方法的底层结构
struct method_t {
SEL name;
const char *types;
IMP imp;
};
struct method_list_t : entsize_list_tt {
method_t first;
};
struct ivar_t {
int32_t *offset;
const char *name;
const char *type;
uint32_t alignment_raw;
uint32_t size;
};
struct ivar_list_t : entsize_list_tt {
ivar_t first;
};
struct property_t {
const char *name;
const char *attributes;
};
struct property_list_t : entsize_list_tt {
property_t first;
};
struct chained_property_list {
chained_property_list *next;
uint32_t count;
property_t list[0];
};
typedef uintptr_t protocol_ref_t;
struct protocol_list_t {
uintptr_t count;
protocol_ref_t list[0];
};
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize; // instance对象占用的内存空间
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name; // 类名
method_list_t * baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars; // 成员变量列表
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
};
struct class_rw_t {
uint32_t flags;
uint32_t version;
const class_ro_t *ro;
method_list_t * methods; // 方法列表
property_list_t *properties; // 属性列表
const protocol_list_t * protocols; // 协议列表
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
};
#define FAST_DATA_MASK 0x00007ffffffffff8UL
struct class_data_bits_t {
uintptr_t bits;
public:
class_rw_t* data() {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
};
/* OC对象 */
struct lc_objc_object {
void *isa;
};
/* 类对象 */
struct lc_objc_class : lc_objc_object {
Class superclass;
cache_t cache;
class_data_bits_t bits;
public:
class_rw_t* data() {
return bits.data();
}
lc_objc_class* metaClass() {
return (lc_objc_class *)((long long)isa & ISA_MASK);
}
};
#endif /* ClassInfo_h */
创建项目并把Object的C++代码导入项目。
#import <Foundation/Foundation.h>
#import "ClassInfo.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [[Person alloc] init];
lc_objc_class *personClass = (__bridge lc_objc_class *)[Person class];
[person personTest];
cache_t cache = personClass->cache;
bucket_t *buckets = cache._buckets;
NSLog(@"%d %d",cache._mask,cache._occupied);
for (int i = 0; i <= cache._mask; i++) {
bucket_t bucket = buckets[i];
NSLog(@"%s %p", bucket._key, bucket._imp);
}
NSLog(@"-----------");
}
return 0;
}
打印如下
2021-09-26 20:27:15.416361+0800 Interview01-cache[35703:1646916] init 0x7ffe2041bc45
2021-09-26 20:27:29.823632+0800 Interview01-cache[35703:1646916] personTest 0x7e78
在控制台中就可以看到缓存列表中缓存的方法,在调用personTest前,方法缓存列表中只有init方法,在调用personTest后,personTest方法就会存储到方法列表中,方便下次快速调用。