一、cache的数据结构
cache_t的源码
通过猜测加分析可知cache_t主要通过操作bucket_t来实现缓存
bucket_t的源码
果然,一切都是bucket_t
二、cache底层LLDB分析
历尽千辛万苦找到buckets(),但是里面是空的?why?哈哈,这个是缓存,没调用国函数,所以没有
再来
通过sel()和imp()函数可以看到已经缓存进去了
三、脱离源码分析
把关于cache_t的结构体复制过来重新定义一下
然后去使用
可以看到_occupied和_maybeMask已经有数据了(自定义结构体中的isa必须要写,因为OC默认第一个是isa,如果不写类型强转之后就会对不上)
四、cache底层原理分析
先看几种情况
1.调用一次函数 [p say1] _occupied = 1 _maybeMask = 3 缓存函数say1
2.调用两次函数 [p say1] [p say2] _occupied = 2 _maybeMask = 3 缓存函数say1 say2
3.调用三次函数 [p say1] [p say2] [p say3] _occupied = 1 _maybeMask = 7 缓存函数say3
4.调用五次函数 [p say1] [p say2] [p say3] [p say4] [p say1] _occupied = 3 _maybeMask = 7 缓存函数say4 say1 say3
_occupied是当前占用,_maybeMask总的大小
问题:
1.为什么是1-3 3-7这种情况?
2.null-0x0 方法去哪了?
3.类方法没在这里面?
- 读取机制
源码追踪:
问题1
cache_t结构体->insert函数
第一次进来 newOccupied = 1 函数走isConstantEmptyCache,INIT_CACHE_SIZE = 4,capacity = 4,然后走reallocate函数
可以看到 setBucketsAndMask(newBuckets, newCapacity - 1);
最终把newBuckets的地址指针存储到_bucketsAndMaybeMask结构体中
初始化之后insert函数中
buckets()函数是获取_bucketsAndMaybeMask中的数据
mask_t m = capacity - 1; 也就是 m=4-1=3,也就是第一次打印1-3中3的原因
接着往下走 do-while循环,第一次进来走这个判断(fastpath(b[i].sel() == 0))
incrementOccupied函数就是_occupied++,也就是第一次打印1-3中1的原因
还有一段代码,当发现内存小于等于当前的3/4时,当前的bucket会扩容2倍
问题2 方法为什么清空?
当扩内存的时候,原有的内存直接清空,底层逻辑是,旧的bucket直接销毁,重新创建一个新的bucket,并且内存大小是旧的2倍,如果把旧的平移到新的bucket中,消耗的性能太大了,并且bucket中存储在前面的是最近调用的方法(顺序不确定,因为是哈希),旧的方法调用的机会也不是太大了,所以直接销毁
问题3 类方法不在(类方法缓存在元类中的cache中的。。。)
问题4 读取机制
断点insert函数(查看堆栈信息)-> log_and_fill_cache(全局搜索) -> lookUpImpOrForward,最终实在消息发送的流程中insert的,
补充:结构体bucket取值$3[2]可以以这样的形式,其实是地址偏移,并不是数组
最后借用一张cache_t的流程图
最后来一个经典的面试题
输出结果:1 0 0 0 1 1 1 1
先看isKindOfClass和isMemberOfClass的源码
分析:
1.[[NSObject class] isKindOfClass:[NSObject class]]类方法调用
第一次 tcls=NSObject元类 cls=NSObject 不相等
第二次 tcls=NSObject cls=NSObject 返回true迭代结束(根元类的父类是NSObject)
2.[[NSObject class] isMemberOfClass:[NSObject class]]类方法调用
tcls=根元类 cls=NSObject 返回false
3.[[LGPerson class] isKindOfClass:[LGPerson class]]类方法调用
第一次 tcls=LGPerson元类 cls=LGPerson 不相等
第二次 tcls=NSObject元类 cls=LGPerson 不相等
第三次 tcls=NSObject cls=LGPerson 不相等 迭代结束 返回false
4.[[LGPerson class] isMemberOfClass:[LGPerson class]]类方法调用
tcls=LGPerson元类 cls=LGPerson 返回false
5.[[NSObject alloc] isKindOfClass:[NSObject class]]
tcls=NSObject cls=NSObject 返回true
6.[[NSObject alloc] isMemberOfClass:[NSObject class]]
tcls=NSObject cls=NSObject 返回true
7.[[LGPerson alloc] isKindOfClass:[LGPerson class]]
tcls=LGPerson cls=LGPerson 返回true
8.[[LGPerson alloc] isMemberOfClass:[LGPerson class]]
tcls=LGPerson cls=LGPerson 返回true
你以为这就是结果?哈哈,
在isKindOfClass打断点,开启汇编调试
系统调的竟然是 objc_opt_isKindOfClass函数。。。再找
你学废了吗。。。不懂的再去看下isa的走位图。。。我先去拉个稀