#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface TDPerson : NSObject
@property (nonatomic, copy) NSString *lgName;
@property (nonatomic, strong) NSString *nickName;
-(void)sayHello;
- (void)sayCode;
- (void)sayMaster;
- (void)sayNB;
+ (void)sayHappy;
@end
NS_ASSUME_NONNULL_END
#import "TDPerson.h"
@implementation TDPerson
- (void)sayHello{
NSLog(@"LGPerson say : %s",__func__);
}
- (void)sayCode{
NSLog(@"LGPerson say : %s",__func__);
}
- (void)sayMaster{
NSLog(@"LGPerson say : %s",__func__);
}
- (void)sayNB{
NSLog(@"LGPerson say : %s",__func__);
}
+ (void)sayHappy{
NSLog(@"LGPerson say : %s",__func__);
}
@end
复制代码
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits;
}
#define CACHE_MASK_STORAGE_OUTLINED 1
#define CACHE_MASK_STORAGE_HIGH_16 2
#define CACHE_MASK_STORAGE_LOW_4 3
#if defined(__arm64__) && __LP64__ //真机64位
#define CACHE_MASK_STORAGE CACHE_MASK_STORAGE_HIGH_16
#elif defined(__arm64__) && !__LP64__ //真32位
#define CACHE_MASK_STORAGE CACHE_MASK_STORAGE_LOW_4
#else //模拟器/mac
#define CACHE_MASK_STORAGE CACHE_MASK_STORAGE_OUTLINED
#endif
struct cache_t {
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED
//模拟器/真机中的cache _buckets和_mask是分开的
//explicit_atomic 原子性,保证增删改查时候的线程安全
//_buckets 查看源码可知里面存储的是sel和imp
explicit_atomic<struct bucket_t *> _buckets;
explicit_atomic<mask_t> _mask;
... //其余都是写掩码 省略
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
//真机将_mask和_buckets写在一块儿为了节省空间
explicit_atomic<uintptr_t> _maskAndBuckets;
mask_t _mask_unused;
... //其余都是写掩码 省略
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
// _maskAndBuckets stores the mask shift in the low 4 bits, and
// the buckets pointer in the remainder of the value. The mask
// shift is the value where (0xffff >> shift) produces the correct
// mask. This is equal to 16 - log2(cache_size).
explicit_atomic<uintptr_t> _maskAndBuckets;
mask_t _mask_unused;
... //其余都是写掩码 省略
}
struct bucket_t {
private:
// IMP-first is better for arm64e ptrauth and no worse for arm64.
// SEL-first is better for armv7* and i386 and x86_64.
#if __arm64__
explicit_atomic<uintptr_t> _imp;
explicit_atomic<SEL> _sel;
#else
explicit_atomic<SEL> _sel;
explicit_atomic<uintptr_t> _imp;
#endif
}
复制代码
#import <Foundation/Foundation.h>
#import "TDPerson.h"
#import <objc/runtime.h>
typedef uint32_t mask_t; // x86_64 & arm64 asm are less efficient with 16-bits
struct td_bucket_t {
SEL _sel;
IMP _imp;
};
struct td_cache_t {
struct td_bucket_t * _buckets;
mask_t _mask;
uint16_t _flags;
uint16_t _occupied;
};
struct td_class_data_bits_t {
uintptr_t bits;
};
struct td_objc_class {
Class ISA;
Class superclass;
struct td_cache_t cache; // formerly cache pointer and vtable
struct td_class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
};
int main(int argc, const char * argv[]) {
@autoreleasepool {
TDPerson *p = [TDPerson alloc];
Class pClass = [TDPerson class]; // objc_clas
// [p say1];
// [p say2];
// [p say3];
[p say4];
struct td_objc_class *td_pClass = (__bridge struct td_objc_class *)(pClass);
NSLog(@"%hu - %u",td_pClass->cache._occupied,td_pClass->cache._mask);
for (mask_t i = 0; i<td_pClass->cache._mask; i++) {
// 打印获取的 bucket
struct td_bucket_t bucket = td_pClass->cache._buckets[i];
NSLog(@"%@ - %p",NSStringFromSelector(bucket._sel),bucket._imp);
}
NSLog(@"Hello, World!");
}
return 0;
}
复制代码
看运行结果打印_occupied 和 _mask,_sel,_imp: _mask是指掩码数据,用于在哈希算法或者哈希冲突算法中计算哈希下标,其中mask 等于capacity - 1c++
ALWAYS_INLINE
void cache_t::insert(Class cls, SEL sel, IMP imp, id receiver)
{
#if CONFIG_USE_CACHE_LOCK
cacheUpdateLock.assertLocked();
#else
runtimeLock.assertLocked();
#endif
ASSERT(sel != 0 && cls->isInitialized());
// Use the cache as-is if it is less than 3/4 full
//第一步计算newOccupied 这里若果没有init和属性赋值(会有一个set方法)的状况下而且是第一次调用方法则occupied() = 0 newOccupied = 1
mask_t newOccupied = occupied() + 1;
//第二步获得当前容量
unsigned oldCapacity = capacity(), capacity = oldCapacity;
//slowpath小几率事件isConstantEmptyCache buckets是空的状况下 建立缓存
if (slowpath(isConstantEmptyCache())) {
// Cache is read-only. Replace it.
//INIT_CACHE_SIZE = (1 << INIT_CACHE_SIZE_LOG2), INIT_CACHE_SIZE_LOG2 =2
//其实就是初始化容量是4
if (!capacity) capacity = INIT_CACHE_SIZE;
//开辟空间
reallocate(oldCapacity, capacity, /* freeOld */false);
}
else if (fastpath(newOccupied + CACHE_END_MARKER <= capacity / 4 * 3)) { // 4 3 + 1 bucket cache_t
// Cache is less than 3/4 full. Use it as-is.
//若是newOccupied + CACHE_END_MARKER = newOccupied + 1的数量小于总容量的3/4的话不须要作处理
//直接走下面的插入流程
}
else {
//大于总容量的3/4则进行扩容(原来容量的两倍)
capacity = capacity ? capacity * 2 : INIT_CACHE_SIZE; // 扩容两倍 4
if (capacity > MAX_CACHE_SIZE) {
capacity = MAX_CACHE_SIZE;
}
reallocate(oldCapacity, capacity, true); // 内存 库容完毕
}
bucket_t *b = buckets();
mask_t m = capacity - 1;
//经过哈希计算bucket_t下标
mask_t begin = cache_hash(sel, m);
mask_t i = begin;
// Scan for the first unused slot and insert there.
// There is guaranteed to be an empty slot because the
// minimum size is 4 and we resized at 3/4 full.
do {
if (fastpath(b[i].sel() == 0)) {
//若是当前下面下面没有存储方法则插入到该位置而后返回
incrementOccupied();
b[i].set<Atomic, Encoded>(sel, imp, cls);
return;
}
if (b[i].sel() == sel) {
//若是当前位置下存储的sel和即将插入的sel相同的话直接返回
// The entry was added to the cache by some other thread
// before we grabbed the cacheUpdateLock.
return;
}
//最后若是发现当前下标下有方法而且和当前要插入的方法不一致则从新计算哈希下标重复此循环
} while (fastpath((i = cache_next(i, m)) != begin));
cache_t::bad_cache(receiver, (SEL)sel, cls);
}
复制代码
从这段代码能够知道方法插入计算下标的时候可能会出现哈希冲突,若是出现哈希冲突就须要从新计算方法的下标,因此在遍历buckets的时候有时候方法打印的顺序不必定就和方法调用的顺序一致注意:init和属性赋值也会插入cache,init自己就是一个方法,属性赋值会触发set方法因此也会存到cache 2. reallocate源码分析 ALWAYS_INLINE
void cache_t::reallocate(mask_t oldCapacity, mask_t newCapacity, bool freeOld)
{
bucket_t *oldBuckets = buckets(); //获得旧的buckets
bucket_t *newBuckets = allocateBuckets(newCapacity); //建立一个新的buckets,此时是个临时的buckets
// Cache's old contents are not propagated.
// This is thought to save cache memory at the cost of extra cache fills.
// fixme re-measure this
ASSERT(newCapacity > 0);
ASSERT((uintptr_t)(mask_t)(newCapacity-1) == newCapacity-1);
//将临时的buckets存到缓存中
setBucketsAndMask(newBuckets, newCapacity - 1);
if (freeOld) {
//释放老的bucket
/**
static void cache_collect_free(bucket_t *data, mask_t capacity)
{
#if CONFIG_USE_CACHE_LOCK
cacheUpdateLock.assertLocked();
#else
runtimeLock.assertLocked();
#endif
if (PrintCaches) recordDeadCache(capacity);
_garbage_make_room (); // 建立垃圾回收空间
garbage_byte_size += cache_t::bytesForCapacity(capacity);
garbage_refs[garbage_count++] = data; //将传入的buckets向后添加
cache_collect(false); //开始释放
}
*/
cache_collect_free(oldBuckets, oldCapacity);
}
}
复制代码
从这里能够发现当须要扩容的时候会将之前存储的方法清理掉,因此上述调用到say3的时候你会发现say1和say2不见了 3. insert流程