根据源码和lldb分析cache_t
查看objc_class源码
struct objc_class : objc_object {
objc_class(const objc_class&) = delete;
objc_class(objc_class&&) = delete;
void operator=(const objc_class&) = delete;
void operator=(objc_class&&) = delete;
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
//后面代码省略
}
可以看到cache前面有ISA和superclass,各占8字节。
添加代码
@interface LGPerson : NSObject
- (void)say1;
- (void)say2;
- (void)say3;
- (void)say4;
- (void)say5;
- (void)say6;
- (void)say7;
+ (void)say11;
@end
@implementation LGPerson
- (void)say1{
NSLog(@"LGPerson say : %s",__func__);
}
- (void)say2{
NSLog(@"LGPerson say : %s",__func__);
}
- (void)say3{
NSLog(@"LGPerson say : %s",__func__);
}
- (void)say4{
NSLog(@"LGPerson say : %s",__func__);
}
- (void)say5{
NSLog(@"LGPerson say : %s",__func__);
}
- (void)say6{
NSLog(@"LGPerson say : %s",__func__);
}
- (void)say7{
NSLog(@"LGPerson say : %s",__func__);
}
+ (void)say11{
NSLog(@"LGPerson say : %s",__func__);
}
@end
LGPerson *p = [LGPerson alloc];
[p say1];
[p say2];
[p say3];
[p say4];
[p say5];
[p say6];
[p say7];
[LGPerson say11];
lldb调试结果
查看
cache_t源码,并查找打印方法
struct cache_t {
private:
explicit_atomic<uintptr_t> _bucketsAndMaybeMask;
union {
struct {
explicit_atomic<mask_t> _maybeMask;
#if __LP64__
uint16_t _flags;
#endif
uint16_t _occupied;
};
explicit_atomic<preopt_cache_t *> _originalPreoptCache;
};
mask_t mask() const;
struct bucket_t *buckets() const;
mask_t occupied() const;
void insert(SEL sel, IMP imp, id receiver);
//中间代码省略
}
根据代码里面的打印方法,打印
发现
buckets()并没有我们想要的值。查看插入函数,发现buckets()是哈希结构。继续打印buckets()
发现只能打印出
say7。查看insert函数,我们发现
void cache_t::insert(SEL sel, IMP imp, id receiver)
{
mask_t newOccupied = occupied() + 1;
unsigned oldCapacity = capacity(), capacity = oldCapacity;
if (slowpath(isConstantEmptyCache())) {
// Cache is read-only. Replace it.
if (!capacity) capacity = INIT_CACHE_SIZE;
reallocate(oldCapacity, capacity, /* freeOld */false);
}
else if (fastpath(newOccupied + CACHE_END_MARKER <= cache_fill_ratio(capacity))) {
// Cache is less than 3/4 or 7/8 full. Use it as-is.
}
#if CACHE_ALLOW_FULL_UTILIZATION
else if (capacity <= FULL_UTILIZATION_CACHE_SIZE && newOccupied + CACHE_END_MARKER <= capacity) {
// Allow 100% cache utilization for small buckets. Use it as-is.
}
#endif
else {
capacity = capacity ? capacity * 2 : INIT_CACHE_SIZE;
if (capacity > MAX_CACHE_SIZE) {
capacity = MAX_CACHE_SIZE;
}
reallocate(oldCapacity, capacity, true);
}
bucket_t *b = buckets();
mask_t m = capacity - 1;
mask_t begin = cache_hash(sel, m);
mask_t i = begin;
//中间代码省略
}
static inline mask_t cache_fill_ratio(mask_t capacity) {
return capacity * 7 / 8; //__arm64__ && !__LP64__为capacity * 3 / 4;
}
#if CACHE_END_MARKER || (__arm64__ && !__LP64__)
// When we have a cache end marker it fills a bucket slot, so having a
// initial cache size of 2 buckets would not be efficient when one of the
// slots is always filled with the end marker. So start with a cache size
// 4 buckets.
INIT_CACHE_SIZE_LOG2 = 2,
#else
// Allow an initial bucket size of 2 buckets, since a large number of
// classes, especially metaclasses, have very few imps, and we support
// the ability to fill 100% of the cache before resizing.
INIT_CACHE_SIZE_LOG2 = 1,
#endif
INIT_CACHE_SIZE = (1 << INIT_CACHE_SIZE_LOG2),
MAX_CACHE_SIZE_LOG2 = 16,
MAX_CACHE_SIZE = (1 << MAX_CACHE_SIZE_LOG2),
FULL_UTILIZATION_CACHE_SIZE_LOG2 = 3,
FULL_UTILIZATION_CACHE_SIZE = (1 << FULL_UTILIZATION_CACHE_SIZE_LOG2),
};
发现 capacity = capacity ? capacity * 2 : INIT_CACHE_SIZE;初始分配内存为INIT_CACHE_SIZE在__arm64__ && !__LP64__下为4,其余为2,当occupied超出分配内存时,会重新分配内存,并将原有内存清空。且mask_t m = capacity - 1;
根据上面条件分析总结猜想(调试机型为M1,初始内存分配为2)
当只调用1个say1 ,occupied为1,mask为1。
当调用2个函数,occupied为2,mask为1。
当调用3个函数,occupied为1,mask为3。
当调用4个函数,occupied为2,mask为3。
当调用5个函数,occupied为3,mask为3。
当调用6个函数,occupied为4,mask为3。
当调用7个函数,occupied为1,mask为7。
脱离源码分析cache_t
如果能用NSLog的方式打印出结果,会节省很多调试时间,基于这个想法,我们将源码自己写一遍。(过程中尽量略去不需要代码)
typedef uint32_t mask_t; // x86_64 & arm64 asm are less efficient with 16-bits
struct kc_bucket_t {
IMP _imp;
SEL _sel;
};
template <typename T>
struct explicit_atomic : public std::atomic<T> {
explicit explicit_atomic(T initial) noexcept : std::atomic<T>(std::move(initial)) {}
operator T() const = delete;
T load(std::memory_order order) const noexcept {
return std::atomic<T>::load(order);
}
void store(T desired, std::memory_order order) noexcept {
std::atomic<T>::store(desired, order);
}
static explicit_atomic<T> *from_pointer(T *ptr) {
static_assert(sizeof(explicit_atomic<T> *) == sizeof(T *),
"Size of atomic must match size of original");
explicit_atomic<T> *atomic = (explicit_atomic<T> *)ptr;
ASSERT(atomic->is_lock_free());
return atomic;
}
};
struct kc_cache_t {
explicit_atomic<uintptr_t> _bucketsAndMaybeMask; // 8
mask_t _maybeMask; // 4
uint16_t _flags; // 2
uint16_t _occupied; // 2
//下面是M1机型机构下需要添加的代码,其它机型根据条件添加
static constexpr uintptr_t maskShift = 48;
static constexpr uintptr_t maxMask = ((uintptr_t)1 << (64 - maskShift)) - 1;
static constexpr uintptr_t bucketsMask = ((uintptr_t)1 << maskShift) - 1;
struct kc_bucket_t *buckets() const;
mask_t mask() const;
};
struct kc_bucket_t *kc_cache_t::buckets() const
{
uintptr_t addr = _bucketsAndMaybeMask.load(std::memory_order_relaxed);
return (kc_bucket_t *)(addr & bucketsMask);
}
mask_t kc_cache_t::mask() const
{
uintptr_t maskAndBuckets = _bucketsAndMaybeMask.load(std::memory_order_relaxed);
return maskAndBuckets >> maskShift;
}
struct kc_class_data_bits_t {
uintptr_t bits;
};
struct kc_objc_class {
Class isa;
Class superclass;
struct kc_cache_t cache; // formerly cache pointer and vtable
struct kc_class_data_bits_t bits;
};
int main(int argc, const char * argv[]) {
@autoreleasepool {
LGPerson *p = [LGPerson alloc];
Class pClass = [LGPerson class];
[p say1];
[p say2];
[p say3];
[p say4];
[p say5];
[p say6];
[p say7];
struct kc_objc_class *kc_class = (__bridge struct kc_objc_class *)(pClass);
NSLog(@"%hu - %u",kc_class->cache._occupied,kc_class->cache.mask());
NSInteger i = kc_class->cache._occupied;
NSInteger count = kc_class->cache.mask();
for (i = 0; i <= count; i ++) {
SEL sel = kc_class->cache.buckets()[i]._sel;
NSString *str = NSStringFromSelector(sel);
NSLog(@"i = %li buckets = %@ %p",(long)i,str,kc_class->cache.buckets()[i]._imp);
}
}
return 0;
}
打印结果
关于类方法的猜想以及验证
前面我们知道了,类方法是存在元类里面的,在类里面我们没有找到类方法,那么类方法是否存在元类里。修改代码
int main(int argc, const char * argv[]) {
@autoreleasepool {
LGPerson *p = [LGPerson alloc];
Class pClass = p.class;
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
[LGPerson say11];
struct kc_objc_class *kc_class = (__bridge struct kc_objc_class *)(metaClass);
NSLog(@"%hu - %u",kc_class->cache._occupied,kc_class->cache.mask());
NSInteger i = kc_class->cache._occupied;
NSInteger count = kc_class->cache.mask();
for (i = 0; i <= count; i ++) {
SEL sel = kc_class->cache.buckets()[i]._sel;
NSString *str = NSStringFromSelector(sel);
NSLog(@"i = %li buckets = %@ %p",(long)i,str,kc_class->cache.buckets()[i]._imp);
}
}
return 0;
}
打印结果
可以在元类里面找到
类方法say11,印证猜想。