本文主要内容
1.通过源码分析cache的缓存内容
2.cache扩容的引出
3.cache_t解析
4.cache扩容规则解析
5.真机测试cache缓存规则
本节内容较难理解,请大家仔细阅读结合源码多看几遍,认真理解!
一测试抑郁症、通过源码分析cache的缓存内容
类对象的objc_class
结构体中包括ISA
、superclass
、cache
、bits
这几个成员,在前几篇文章中,我们对ISA
有了一定的了解,也研究了包含实例方法、属性、协议等内容
的bit架构图怎么画s成员
,下面我们来研究该结构体中的另一个成员:cache
。
查看objc4-838.1架构图
源码,如果熟读前面的文章,我们测试工程师很容易知道,类对象的内存地ios是什么意思址向右平移16个字节即可得到cache的地址
。
找到cache
的内存地址,通过强转为cache_t
类型并读取cache
的数据内容。
找到cache_t
的源码发现打印的内容即为其结构体的成员。但仍然无法了解这些成员的含义。根据前面的经验,我们去分析cache_t
提供的方法。
经过查看cache_t
提供的方法,找到一个叫作insert
的函数。在insert
方法中注意到bucket_t *b = buckets()
,源码交易平台而bucket_t
存储的内容就是方法名和方法的实现
。接着是对对象b
进行set
操作,操作对象b
时使用数组的格式b[i]
调用,所以要分析i
如何取值架构图怎么制作。
现在开始分析i
的取值,首先计算mask_t m = capacity - 1;
,已知_maybeMask = bu测试抑郁症的20道题cket_t长度-1(后源码交易平台续详述),所以capacity = oldCapacity =capacity() = mask()数组初始化+1 =_maybeMask+1=buc测试抑郁症ket_t长度-1+1= bucket_t
,所以m
的值为bucket_t-1
。
接着测试抑郁程度的问卷计算mask_t begin = cache_hash(sel, m);
,cache_源码hash
算法函数中,先把sel
转换成uintptr_t数组指针
类型即unsigned long源码交易平台
类型的值value
(该数字很大
)。因此value&mask
的结果最大值是mask。所以begin
的值最大值就是m
即bucket_t-1
。
由此得出i
的值不会超过bucket_t-1
。
验证把"sel"转换成uintptr_t类型即unsigned long类型的数字value很大!
上述的set
操作实际上就是对方法的存储。为什么说对对象b
进行set
操作实际上就是对方法的保存?在set
方法中,分别调用了_imp_store和_sel_store函数。
为什么会找到insert的方法?首先cache表示缓存,所以肯定会涉及到插入、删除等操作,这样就顺其自然的关注到这个方法.各位读者经过长期跟随作者的底层源码分析,也会逐渐形成这样的举一反三的思维能力!
总结整个过程:通过buckets()
获取到bucket_t *
类型的架构图模板b
,确定b的取值范围i最大为bucket架构师和程序员的区别_t长度-1
,判断通过哈希算法得到的i对应的数组去重方法b[i]的sel是否有值,如果没有值测试仪,通过set函数将imp和sel存入b[i]中;如果b[i]的sel是否等于当前要存储的sel,如果相同说明已经缓存,直接返回;如果b[i]的sel有值并且不等于当前要存储的sel则调用 cache_next
函数,当前要存储的ios是什么意思sel存储到下标为i+1
的对象中。
由此得出结论:cache是用来缓存方法的,即sel和imp!
二、cache扩容的引出
既然cache
是用来缓存方法的,那么cache
把方法存储在什么地方呢?
直接读取cache中的成员发现,方法sel和imp并无法获取到。
根据经验会想到去找函数测试抑郁程度的问卷来获取。由一、通过源码分析cache的缓存内容
中发现,存储方法sel
和imp
的是一个bucket_t
的结构体类型。在源码中找到返回bucket_t
类型数据的buckets()
函数,调用该函数并读取源码之家返回的内存内容,发现确实是存储了sel
和imp
,首先存储有class
方法。
继续读取,发现存储了respondsToSelector:
方法。
三.cache_t解析
cache_t
结构体中包含_bucketsAndMaybeM架构师和程序员的区别ask
(保证对数据增删改查时线程安全)占用8字节,还有1个联合体,其中包含4个成员:mask_t
类型即unsigned int
的_maybeMask
占用4个字节、uint16_数组排序t
类型即uios越狱nsigned short
类型的_flags
占用2个字节、uint16_t
类型即unsigned short
类型测试工程师的_occupied
占用2个字节、preo架构图怎么制作pt_cache_t *
(指针)类型的_originalPreoptCache
占用8个字数组和链表的区别节,所以cache_t
结构体总共为16字节!所以源码交易平台,可以验证通过类对象内存平移16个字节即可找到cache得内存地址
。
四.cache扩容规则解析
在二、cache扩容的引出
中分析方法的存储时,调用方法method1
后,可能只会找到方法class
和方法respon架构图模板dsToSelector
的存储而找不到metho源码网站d1
。这是因为cache进行了扩容。
进入之前研究的insert
函数中,在存储方法的s测试抑郁程度的问卷el
和imp
之前,即bucket_t *b = bu源码交易平台ckets();
之前实现了cache扩容的数组排序逻辑。
从mask_t newOccupied = occupied() + 1;
开始分析,occupiedios模拟器()
返回cache_t
的成员变量_occupied
,当第一次进入insert
函数时ios应用商店,_occupied
为0也就是occupied()
为0,所以newOccupied
值为1;oldCapacity
等于bucket_t
的长度。
首次调用数组去重方法会进入第测试英文一个if
(if (slowpath(ios系统isConsta源码编辑器ntEmptyCache()))
),如图中1
: INIios是苹果还是安卓T_CACHE_数组和链表的区别SIZE
在x86_64
架构下为1<<2(4
),arm64架构下1<<1(2
),即capacity在在x8数组指针6_64
架构下为4
,arm64架构下2
,再调用reallocate
函数判断是否需测试你适合学心理学吗要释放oldBuckets
。即第一次调用insert
时,在x86_64架构下开辟一个长度为4的桶子,在arm64架构下开辟一个长度为2的桶子源码时代。
如图中2
,cache_f测试你的自卑程度ill_ratio
在x86_64
架构下为bucket_t
长度的3/4
,在ar数组去重方法m64
架构下为bucket_t
长度的7/8
。所以如果缓存的大小在x86_64
架构下小于等于桶子长度的3/4
,在arm64
架ios14.4.1更新了什么构下小于桶子长度的7/8
,在arm64
架构下桶子长度小于等于8时不做任何操作。源码编程器
否则,如源码图中3
,会进行扩容。释放oldBuckets
.
总结:cache扩容规则
- 在x86_64架构下,当缓存的大小等于桶子长度的3/4的时候,进行两倍扩容;
- 在arm64架构下,当缓存的大小大于桶子长度的7/8的时候,进行两倍扩容;桶子长度小于等于8时不会扩容。
疑难问题:cache扩容逻辑需要根据源码多次分析理解!
五.真机测试cache缓存规则
通数组词过仿写源码中的objc_class
结构iOS体,其中也包括仿写ISA
、superclass
、cache
、bits
这几个成员,利用仿写的objc_class
结构体对Class
进行转换,这样数组初始化就可以获iOS取到仿测试抑郁症的20道题写的cache
中的数据。如下为部分代码供参考:
typedef uint32_t mask_t; // x86_64 & arm64 asm are less efficient
//preopt_cache_entry_t源码模仿
struct lg_preopt_cache_entry_t {
uint32_t sel_offs;
uint32_t imp_offs;
};
//preopt_cache_t源码模仿
struct lg_preopt_cache_t {
int32_t fallback_class_offset;
union {
struct {
uint16_t shift : 5;
uint16_t mask : 11;
};
uint16_t hash_params;
};
uint16_t occupied : 14;
uint16_t has_inlines : 1;
uint16_t bit_one : 1;
struct lg_preopt_cache_entry_t entries;
inline int capacity() const {
return mask + 1;
}
};
//bucket_t源码模仿
struct lg_bucket_t {
IMP _imp;
SEL _sel;
};
//cache_t源码模仿
struct lg_cache_t {
uintptr_t _bucketsAndMaybeMask; // 8
struct lg_preopt_cache_t _originalPreoptCache; // 8
// _bucketsAndMaybeMask is a buckets_t pointer in the low 48 bits
// _maybeMask is unused, the mask is stored in the top 16 bits.
// How much the mask is shifted by.
static constexpr uintptr_t maskShift = 48;
// Additional bits after the mask which must be zero. msgSend
// takes advantage of these additional bits to construct the value
// `mask << 4` from `_maskAndBuckets` in a single instruction.
static constexpr uintptr_t maskZeroBits = 4;
// The largest mask value we can store.
static constexpr uintptr_t maxMask = ((uintptr_t)1 << (64 - maskShift)) - 1;
// The mask applied to `_maskAndBuckets` to retrieve the buckets pointer.
static constexpr uintptr_t bucketsMask = ((uintptr_t)1 << (maskShift - maskZeroBits)) - 1;
static constexpr uintptr_t preoptBucketsMarker = 1ul;
// 63..60: hash_mask_shift
// 59..55: hash_shift
// 54.. 1: buckets ptr + auth
// 0: always 1
static constexpr uintptr_t preoptBucketsMask = 0x007ffffffffffffe;
lg_bucket_t *buckets() {
return (lg_bucket_t *)(_bucketsAndMaybeMask & bucketsMask);
}
uint32_t mask() const {
return _bucketsAndMaybeMask >> maskShift;
}
};
//class_data_bits_t源码模仿
struct lg_class_data_bits_t {
uintptr_t objc_class;
};
//类源码模仿
struct lg_objc_class {
Class isa;
Class superclass;
struct lg_cache_t cache;
struct lg_class_data_bits_t bits;
};
如上通过对源码的仿写,对cache扩容规数组排序则进行了验证,即在arm64架构下(iOS为arm64架构)测试抑郁程度的问卷,当缓存的大小大于桶子长度的7/8的时候,进行两倍扩容;桶子长度小于等于8架构图怎么制作时不会扩容!
本文总结
1.cache是用来缓存方法的,缓存源码时代使方法的调用源码中的图片更高效;
2.在x86_64架构测试抑郁症的20道题下,当缓存的大小等于桶测试工程师子长度的3/4的时候,进行两倍扩容;
3.在arm64架构下,当缓存的大小大于桶子长度的7/8的时候,进行两倍扩容;桶子长度小于等于8时不会扩容。
有任架构师证书何问题,欢迎各位评论指出!觉得博主写的还不错的麻烦点个赞喽