OC底层原理(八):objc_msgSend快速查找的分析下

558 阅读10分钟

本章节是对于上个章节的补充说明,通过流程图与真机汇编解析快速查找,顺便引出下一章的慢速查找。而最后的cache_t的扩容是对cache_t的补充说明!

一、缓存查询流程图

截屏2022-12-31 上午2.56.57.png

  • 流程图:
graph TB
A[objc_msgSend] -->B{cmp p0, #0<br>判断接受者是否存在?<br>}
    B -->|否| D[通过对象isa获取class]
    B -->|是| C{是否支持<br>taggegpointer对象}
    C -->|是| E{小对象或者空 <br>判断LNilOrTagged<br>}
    D --> G[获取isa完毕 LGetIsaDone]
    E -->|小对象Isa处理| G
    C -->|否| F[直接返回空LReturnZero]
    E -->|空| F
    G --> H[开去缓存查找流程 <br>CacheLookup NORMAL<br>]
    H --> I[通过类的指针平移16得到<br>cacheldr p11,x16 #CACHE<br>]
    I --> J[通过获取的mask_buckets掩码运算得到<br>bucketsand p10,p11, #0x0000ffffffffffff<br>]
    I --> K1[通过逻辑右移得到<br>mask p11, LSR #48<br>]
    K1 --> K2[通过and p12, p1 计算哈希函数<br>得到下标 _cmd &mask<br>]
    J --> K["p12=buckets+((_cmd & mask)<<(1+PTRSHIFT))<br>通过内存平移得到bucket<br>"]
    K2 --> K
    K -->|第一次通过下标向前查找| L["通过bucket结构体得到<br>{imp,sel}=*bucket--<br>"]
    L --> M{判断要查询sel和当前_cmd<br>是否相等<br>}
    M -->|是| N[命中缓存CacheHit NORMAL]
    M -->|否| Q{"sel是否为0(nil)"}
    Q -->|是| W["找不到缓存sel或者结束循环<br>进入慢速查找_objc_msgSend_uncache"]
    Q -->|否| O{"判断当前查询bucket是否为<br>第一个元素<br>bucket == buckets<br>"}
    O -->|是| P["把当前查询bucket设置为最后一个元素<br>p12 = buckets + (mask << 1+PTRSHIFT)<br>"]
    O -->|否| L
    P --> P1["计算出下标index所在bucket(first_probed)"]
    P1 -->|第二次从buckets最后开始向前查找| L1["通过bucket结构体得到  
{imp,sel}=*bucket--"]
    L1 --> M1{判断要查询sel和当前_cmd<br>是否相等<br>}
    M1 -->|是| N
    M1 -->|否| O1{"判断当前查询sel是存在<br>且当前bucket大于first_probed"}
    O1 -->|否| W
    O1 -->|是| L1

二、汇编流程图简化讲解

  1. 判断当前接收者是否存在
cmp	p0, #0
  1. 通过isa拿到class
ldr p14, [x0] // p14 = raw isa
GetClassFromIsa_p16 p14, 1, x0 // p16 = class
  1. 进入CacheLookup流程,两种结果;找到并抛出imp,没找到通过__objc_msgSend_uncached进入后续的慢速查找流程
CacheLookup NORMAL, _objc_msgSend,__objc_msgSend_uncached
  1. LLookupStart查找开始,这里根据模拟器真机arm64架构,A12以上芯片情况做了汇编区分,下面只列出满足条件的汇编
//模拟器
CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16_BIG_ADDRS //mac电脑(x86-64)、arm64的模拟器
//真机
CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
    //amr64真机
    CONFIG_USE_PREOPT_CACHES = 1
    //是判断编译器是否支持指针身份验证功能,针对arm64e
    //A12以上芯片(支持arm64e结构)
     __has_feature(ptrauth_calls)
  1. isa偏移16个字节,由p11 = mask|buckets得到cache_t
ldr	p11, [x16, #CACHE]
  1. 判断cache_t0号位置是否为0?如果为0,执行LLookupPreopt,去找共享缓存;如果为0,将cache_t(_bucketsAndMaybeMask) & #0x0000ffffffffffff,得到了buckets
tbnz	p11, #0, LLookupPreopt\Function
and	p10, p11, #0x0000ffffffffffff	// p10 = buckets
  1. sel右移7位在与自己做逻辑异或^赋值给p12,将cache_t(_bucketsAndMaybeMask)右移48位,可以得到mask,最后将p12 & maskhash算法得到了查找方法_cmd对应buckets的下标index,即first_probed
//p1 = _cmd, eor是异或,p12 = p1 ^(p1 >> 7) = _cmd ^ (_cmd >> 7)
eor p12, p1, p1, LSR #7
//p11 >> 48 = _bucketsAndMaybeMask >> 48 = mask
//p12 = p12 & (p11 >> 48) = _cmd ^ (_cmd >> 7) & mask
//p12 = (_cmd ^ (_cmd >> 7)) & mask 这一步的作用就是hash求下标index
and p12, p12, p11, LSR #48 // x12 = (_cmd ^ (_cmd >> 7)) & mask
  • clang得到c++源码:
static inline mask_t cache_hash(SEL sel, mask_t mask) 
{
    uintptr_t value = (uintptr_t)sel;
#if CONFIG_USE_PREOPT_CACHES
    value ^= value >> 7;
#endif
    return (mask_t)(value & mask);
}
  1. 由于上面可知第一个查找的bucket_t的下标index,那么接下来就是通过偏移到这个bucket_t拿到里面的selimp,这里是将index向左偏移4位,理解为index * (1 << 4)index * 16,得到的是偏移距离。偏移4位是因为一个bucket_t16字节,最后再加上buckets地址,就得到了第一个要比较的bucket_t
add p13, p10, p12, LSL #(1+PTRSHIFT)
                        // p13 = buckets + ((_cmd & mask) << (1+PTRSHIFT))
  1. p13内的impsel拿出来分别存在p17p9,再执行bucket--向前移位即index--。如果sel != _cmd,就执行3f。判断sel是否存在?不存在时就进入慢速查找__objc_msgSend_uncached;存在判断当前bucket是不是buckets范围内?如果在范围内,继续循环,回到1b,取出向前移位的impsel。如果这个过程找到了目标_cmd,跳转到2,命中缓存,结束查找。
                                            // do{
1:	ldp	p17, p9, [x13], #-BUCKET_SIZE  //   {imp, sel} = *bucket--
	cmp	p9, p1			      //   if (sel != _cmd) {
	b.ne	3f			            //      scan more 
                                           //    }
                                           //    else {
2:	CacheHit \Mode                       //     hit:    call or return imp
                                           //    }
3:	cbz	p9, \MissLabelDynamic         //    if (sel == 0) goto Miss;		
	cmp	p13, p10                      //   } while (bucket >= buckets)	
	b.hs	1b
  1. 如果找到bucket首位都未匹配到目标_cmd,则直接跳转至maskbucket(末位bucket_t),重新倒序查找缓存内的bucket_t,这里还获取了第一次探查的位置index,为了避免重复探查。4内操作为将bucket_timpsel拿出来赋值给p17p9,再执行bucket--向前查找缓存方法,比较sel_cmd是否相同?如果相同,执行2b,命中缓存;如果不同就判断p9sel是否存在?与判断当前bucket是否大于第一次探查的bucket?,如果上述条件都满足,则继续循环4b,直到命中缓存。如果全部都搜索完还没有命中,进入慢速查找__objc_msgSend_uncached流程。
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16///arm64真机
add p13, p10, p11, LSR #(48 - (1+PTRSHIFT))
                    // p13 = buckets + (mask << 1+PTRSHIFT)

add p12, p10, p12, LSL #(1+PTRSHIFT)
                    // p12 = first probed bucket
                                    // do {
4:	ldp	p17, p9, [x13], #-BUCKET_SIZE	//  {imp, sel} = *bucket--
	cmp	p9, p1				//  if (sel == _cmd)
	b.eq	2b				      //      goto hit
	cmp	p9, #0				// } while (sel != 0 &&
	ccmp	p13, p12, #0, ne		      // bucket > first_probed)
	b.hi	4b

三、真机调试(汇编分析)

  • 案例源码:
#import <UIKit/UIKit.h>
#import "AppDelegate.h"

@interface CJPerson : NSObject
- (void)saySomething;
@end

@implementation CJPerson
- (void)saySomething{
    NSLog(@"%s",__func__);
}
@end
int main(int argc, char * argv[]) {
    @autoreleasepool {

        CJPerson *p = [CJPerson alloc];
        [p saySomething];
        appDelegateClassName = NSStringFromClass([AppDelegate class]);
    }
    return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}

确认当前探索的类与方法

截屏2021-07-19 上午10.28.49.png

真机汇编分解图解:

libobjc.A.dylib`objc_msgSend:
    //x0为消息接收者receiver,比较x0与0,如果没有接收者,则此次objc_msgSend没有意义(x0 = 0x00000002803b8180)
->  0x19ef9b1c0 <+0>:   cmp    x0, #0x0                  ; =0x0 

    0x19ef9b1c4 <+4>:   b.le   0x19ef9b28c               ; <+204>
    //将x0里面的实例对象p的isa赋值给x13
    //即将寄存器x0地址为0x00000002803b8180的字数据读入到寄存器x14(x14 = 0x01000001026cd441 (0x00000001026cd441) (void *)0x4800000001026cd4)
    //具体可以看`底层原理(三)`里的x/4gx 0x00000002803b8180
    0x19ef9b1c8 <+8>:   ldr    x14, [x0]
    //将x14&0x7ffffffffffff8(即isa & ISA_MASK获取到类CJPerson)赋值给x16(x16 = 0x00000001026cd440  (void *)0x00000001026cd418: CJPerson)
    0x19ef9b1cc <+12>:  and    x16, x14, #0x7ffffffffffff8
    //将 x0赋值给x10(即将实例对象p复制到x10 = 0x00000002803b8180)
    0x19ef9b1d0 <+16>:  mov    x10, x0
    //将x10内的内容数据从48位开始更为0x6ae1(x10 = 0x6ae10002803b8180)
    0x19ef9b1d4 <+20>:  movk   x10, #0x6ae1, lsl #48
    //(x16 = 0x00000001026cd440  (void *)0x00000001026cd418: CJPerson)
    0x19ef9b1d8 <+24>:  autda  x16, x10
    //将x16赋值给x15(x15 = 0x00000001026cd440  (void *)0x00000001026cd418: CJPerson)
    0x19ef9b1dc <+28>:  mov    x15, x16
    //将CJPerson的isa右移16位读取cache_t, px16+0x10(x/4gx 0x00000001026cd440)后读取内容数据到(x11 = 0x00010002801be4a0)
    0x19ef9b1e0 <+32>:  ldr    x11, [x16, #0x10]
    //判断w11(w11 = 0x801be4a0)的第0位是否为0,为0继续往下走,不为0跳转到下面0x19ef9b240地址执行
    0x19ef9b1e4 <+36>:  tbnz   w11, #0x0, 0x19ef9b240    ; <+128>
    //x10 = x11 & 0xffffffffffff(即x10 = 0x00000002801be4a0)
    0x19ef9b1e8 <+40>:  and    x10, x11, #0xffffffffffff
    //x12 = _cmd ^ (_cmd >> 7)即(传入方法参数x1 = 0x00000001026c718e  "saySomething")(x12为0x000000010068a96d = 0x00000001026c718e ^ 0x000000000204d8e3)
    0x19ef9b1ec <+44>:  eor    x12, x1, x1, lsr #7
    //mask = x11 >> 48 即(0x00010002801be4a0 >> 48 = 0x0000000000000001)
    //x12 = mask & x12 即(x12 = 0x0000000000000001)为第一个检索位置即first_prepot
    0x19ef9b1f0 <+48>:  and    x12, x12, x11, lsr #48
    //buckets + first_preop 即 x13 = (0x00000002801be4a0 + (0x01 << 4 = 0x10)) = 0x00000002801be4b0
    0x19ef9b1f4 <+52>:  add    x13, x10, x12, lsl #4
    //p x13 - 0x10得到老版本地区 即x/4gx 0x00000002801be4b0取出前两位分别赋值 x17 = 0xdd1a3d019efbeb34, x9 = 0x00000001da0a38ae
    0x19ef9b1f8 <+56>:  ldp    x17, x9, [x13], #-0x10
    //比较x9与x1,x9 == x1 即0x00000001da0a38ae == 0x00000001026c718e
    0x19ef9b1fc <+60>:  cmp    x9, x1
    //不等跳转到0x19ef9b210
    0x19ef9b200 <+64>:  b.ne   0x19ef9b210               ; <+80>
    // x10 = x10异或x1
    0x19ef9b204 <+68>:  eor    x10, x10, x1
    // x10 = x10 ^ x16
    0x19ef9b208 <+72>:  eor    x10, x10, x16
    //
    0x19ef9b20c <+76>:  brab   x17, x10
    // 
    0x19ef9b210 <+80>:  cbz    x9, 0x19ef9b600           ; _objc_msgSend_uncached
    //比较x13与x10 即0x00000002801be4a0 == 0x00000002801be4a0
    0x19ef9b214 <+84>:  cmp    x13, x10
    //相等时跳转到0x19ef9b1f8从新开始 但是x13 = 0x00000002801be4a0比之前0x00000002801be4b0-0x10
    0x19ef9b218 <+88>:  b.hs   0x19ef9b1f8               ; <+56>
    //不相等时,x13 = (x10 = 0x00000002801be4a0) + ((x11 = 0x00010002801be4a0)>>44 = 0x0000000000000010) = 0x00000002801be4b0
    0x19ef9b21c <+92>:  add    x13, x10, x11, lsr #44
    //x12 = (x10 = 0x00000002801be4a0) + (x12 = 0x0000000000000001 << 4) = 0x00000002801be4b0
    0x19ef9b220 <+96>:  add    x12, x10, x12, lsl #4
    //取出x13指向数据前两位x17 = 0xdd1a3d019efbeb34, x9 = 0x00000001da0a38ae
    0x19ef9b224 <+100>: ldp    x17, x9, [x13], #-0x10
    //比较x9与x1 x9 = 0x00000001da0a38ae x1 = 0x00000001026c718e  "saySomething"
    0x19ef9b228 <+104>: cmp    x9, x1
    //相等时跳转0x19ef9b204
    0x19ef9b22c <+108>: b.eq   0x19ef9b204               ; <+68>
    //判断x9是否为0x00
    0x19ef9b230 <+112>: cmp    x9, #0x0                  ; =0x0 
    //x13 = 0x00000002801be4a0, x12 = 0x00000002801be4b
    0x19ef9b234 <+116>: ccmp   x13, x12, #0x0, ne

    0x19ef9b238 <+120>: b.hi   0x19ef9b224               ; <+100>
    //跳转到0x19ef9b600执行_objc_msgSend_uncached(慢速查找)
    0x19ef9b23c <+124>: b      0x19ef9b600               ; _objc_msgSend_uncached
    //x10 = x11 & 0x7ffffffffffffe
    0x19ef9b240 <+128>: and    x10, x11, #0x7ffffffffffffe
    
    0x19ef9b244 <+132>: autdb  x10, x16

    0x19ef9b248 <+136>: adrp   x9, 239235

    0x19ef9b24c <+140>: add    x9, x9, #0x4ae            ; =0x4ae 

    0x19ef9b250 <+144>: sub    x12, x1, x9

    0x19ef9b254 <+148>: lsr    x17, x11, #55

    0x19ef9b258 <+152>: lsr    w9, w12, w17

    0x19ef9b25c <+156>: lsr    x17, x11, #60

    0x19ef9b260 <+160>: mov    x11, #0x7fff

    0x19ef9b264 <+164>: lsr    x11, x11, x17

    0x19ef9b268 <+168>: and    x9, x9, x11

    0x19ef9b26c <+172>: ldr    x17, [x10, x9, lsl #3]

    0x19ef9b270 <+176>: cmp    x12, w17, uxtw

    0x19ef9b274 <+180>: b.ne   0x19ef9b280               ; <+192>

    0x19ef9b278 <+184>: sub    x17, x16, x17, lsr #32

    0x19ef9b27c <+188>: br     x17

    0x19ef9b280 <+192>: ldursw x9, [x10, #-0x8]

    0x19ef9b284 <+196>: add    x16, x16, x9

    0x19ef9b288 <+200>: b      0x19ef9b1e0               ; <+32>

    0x19ef9b28c <+204>: b.eq   0x19ef9b2b0               ; <+240>

    0x19ef9b290 <+208>: and    x10, x0, #0x7

    0x19ef9b294 <+212>: asr    x11, x0, #55

    0x19ef9b298 <+216>: cmp    x10, #0x7                 ; =0x7 

    0x19ef9b29c <+220>: csel   x12, x11, x10, eq

    0x19ef9b2a0 <+224>: adrp   x10, 320424

    0x19ef9b2a4 <+228>: add    x10, x10, #0x820          ; =0x820 

    0x19ef9b2a8 <+232>: ldr    x16, [x10, x12, lsl #3]

    0x19ef9b2ac <+236>: b      0x19ef9b1dc               ; <+28>

    0x19ef9b2b0 <+240>: mov    x1, #0x0

    0x19ef9b2b4 <+244>: movi   d0, #0000000000000000

    0x19ef9b2b8 <+248>: movi   d1, #0000000000000000

    0x19ef9b2bc <+252>: movi   d2, #0000000000000000

    0x19ef9b2c0 <+256>: movi   d3, #0000000000000000

    0x19ef9b2c4 <+260>: ret    

    0x19ef9b2c8 <+264>: nop    

    0x19ef9b2cc <+268>: nop    

    0x19ef9b2d0 <+272>: nop    

    0x19ef9b2d4 <+276>: nop    

    0x19ef9b2d8 <+280>: nop    

    0x19ef9b2dc <+284>: nop    

四、补充内容

关于类cache_t内存扩容在真机情况下的源码模拟

  • 部分关键源码:
typedef uint32_t mask_t;  // x86_64 & arm64 asm are less efficient

//preopt_cache_entry_t源码模仿
struct ff_preopt_cache_entry_t {
    int64_t raw_imp_offs : 38; // actual IMP offset from the isa >> 2
    uint64_t sel_offs : 26;

    inline int64_t imp_offset() const {
        return raw_imp_offs << 2;
    }
};

//preopt_cache_t源码模仿
struct ff_preopt_cache_t {
    int64_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 padding     :  1;
    uint32_t unused      : 31;
    uint16_t bit_one     :  1;
    struct ff_preopt_cache_entry_t entries[];
    
    inline int capacity() const {
        return mask + 1;
    }
};

//bucket_t源码模仿
struct ff_bucket_t {
    IMP _imp;
    SEL _sel;
};

//cache_t源码模仿
struct ff_cache_t {
    uintptr_t _bucketsAndMaybeMask; // 8
    union {
        struct {
            uint32_t                   _unused;
            uint16_t                   _occupied;
            uint16_t                   _flags;
        };
        struct ff_preopt_cache_t *_originalPreoptCache; // 8
    };

    static constexpr uintptr_t maskShift = 48;
    static constexpr uintptr_t maskZeroBits = 4;
    static constexpr uintptr_t maxMask = ((uintptr_t)1 << (64 - maskShift)) - 1;
    static constexpr uintptr_t bucketsMask = ((uintptr_t)1 << (maskShift - maskZeroBits)) - 1;
    static constexpr uintptr_t preoptBucketsMarker = 1ul;
    static constexpr uintptr_t preoptBucketsMask = 0x007ffffffffffffe;
    
    ff_bucket_t *buckets() {
        return (ff_bucket_t *)(_bucketsAndMaybeMask & bucketsMask);
    }
    
    uint32_t mask() const {
        return _bucketsAndMaybeMask >> maskShift;
    }
    
    unsigned capacity() const {
        return mask() ? mask()+1 : 0;
    }
    
    mask_t occupied() const {
        return _occupied;
    }
};

//class_data_bits_t源码模仿
struct ff_class_data_bits_t {
    uintptr_t objc_class;
};

//类源码模仿
struct ff_objc_class {
    Class isa;
    Class superclass;
    struct ff_cache_t cache;
    struct ff_class_data_bits_t bits;
};


void test(Class cls) {
    
    //将person的类型转换成自定义的源码ff_objc_class类型,方便后续操作
    struct ff_objc_class *pClass = (__bridge struct ff_objc_class *)(cls);
 
    struct ff_cache_t cache = pClass->cache;
    struct ff_bucket_t * buckets = cache.buckets();
    uintptr_t _bucketsAndMaybeMask = cache._bucketsAndMaybeMask;
    uintptr_t mask = cache.mask();
    
    
    NSLog(@"class: %p", pClass);
    NSLog(@"_bucketsAndMaybeMask: 0x%lx, mask: %lu", _bucketsAndMaybeMask, mask);
    
    //打印当前有多少个方法缓存与最大缓存数量
    NSLog(@"%lu-%lu",cache.occupied(), cache.capacity());
    
    //打印buckets
    for (int i = 0; i < mask + 1; i++ ) {
        SEL sel = buckets[i]._sel;
        IMP imp = buckets[i]._imp;
        NSLog(@"%@-%p",NSStringFromSelector(sel),imp);
    }
}

int main(int argc, char * argv[]) {

    @autoreleasepool {
        
        //给person分配内存
        FFPerson *person = [FFPerson alloc];
        //调用方法
        [person likeGirls];
        test(person.class);
        [person likeFoods];
        test(person.class);
        [person likeFlower];
        test(person.class);
        [person likeStudy];
        test(person.class);
        [person enjoyLift];
        test(person.class);
        [person lnspireCreativity];
        test(person.class);

        [person likeGirls];
        test(person.class);
        [person likeFoods];
        test(person.class);
        [person likeFlower];
        test(person.class);
        [person likeStudy];
        test(person.class);
        [person enjoyLift];
        test(person.class);
        [person lnspireCreativity];
        test(person.class);
        [person likePlay];
        test(person.class);
        [person likeSleep];
        test(person.class);
        [person likeRead];
        test(person.class);
        appDelegateClassName = NSStringFromClass([AppDelegate class]);
    }
    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
  • 打印结果:
2023-01-01 03:26:22.960430+0800 objc_test[4652:874431] -[FFPerson likeGirls]
2023-01-01 03:26:22.961419+0800 objc_test[4652:874431] class: 0x1002f10e0
2023-01-01 03:26:22.961476+0800 objc_test[4652:874431] _bucketsAndMaybeMask: 0x1000281eae820, mask: 1
2023-01-01 03:26:22.961505+0800 objc_test[4652:874431] 1-2
2023-01-01 03:26:22.961532+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:22.961688+0800 objc_test[4652:874431] likeGirls-0x1064a881002e941c
2023-01-01 03:26:22.961721+0800 objc_test[4652:874431] -[FFPerson likeFoods]
2023-01-01 03:26:22.961745+0800 objc_test[4652:874431] class: 0x1002f10e0
2023-01-01 03:26:22.961770+0800 objc_test[4652:874431] _bucketsAndMaybeMask: 0x1000281eae820, mask: 1
2023-01-01 03:26:22.961801+0800 objc_test[4652:874431] 2-2
2023-01-01 03:26:22.961868+0800 objc_test[4652:874431] likeFoods-0xfc701301002e9458
2023-01-01 03:26:22.961917+0800 objc_test[4652:874431] likeGirls-0x1064a881002e941c
2023-01-01 03:26:22.961997+0800 objc_test[4652:874431] -[FFPerson likeFlower]
2023-01-01 03:26:22.962146+0800 objc_test[4652:874431] class: 0x1002f10e0
2023-01-01 03:26:22.962199+0800 objc_test[4652:874431] _bucketsAndMaybeMask: 0x3000280ba0040, mask: 3
2023-01-01 03:26:22.962259+0800 objc_test[4652:874431] 1-4
2023-01-01 03:26:22.962493+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:22.962608+0800 objc_test[4652:874431] likeFlower-0xbd2e4f81002e9494
2023-01-01 03:26:22.962658+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:22.962734+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:22.962815+0800 objc_test[4652:874431] -[FFPerson likeStudy]
2023-01-01 03:26:22.962850+0800 objc_test[4652:874431] class: 0x1002f10e0
2023-01-01 03:26:22.962927+0800 objc_test[4652:874431] _bucketsAndMaybeMask: 0x3000280ba0040, mask: 3
2023-01-01 03:26:22.962977+0800 objc_test[4652:874431] 2-4
2023-01-01 03:26:22.963027+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:22.963257+0800 objc_test[4652:874431] likeFlower-0xbd2e4f81002e9494
2023-01-01 03:26:22.963307+0800 objc_test[4652:874431] likeStudy-0x5015af01002e94d0
2023-01-01 03:26:22.963370+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:22.963467+0800 objc_test[4652:874431] -[FFPerson enjoyLift]
2023-01-01 03:26:22.963495+0800 objc_test[4652:874431] class: 0x1002f10e0
2023-01-01 03:26:22.963568+0800 objc_test[4652:874431] _bucketsAndMaybeMask: 0x3000280ba0040, mask: 3
2023-01-01 03:26:22.963617+0800 objc_test[4652:874431] 3-4
2023-01-01 03:26:22.963733+0800 objc_test[4652:874431] enjoyLift-0x925c6081002e950c
2023-01-01 03:26:22.963844+0800 objc_test[4652:874431] likeFlower-0xbd2e4f81002e9494
2023-01-01 03:26:22.963890+0800 objc_test[4652:874431] likeStudy-0x5015af01002e94d0
2023-01-01 03:26:22.963918+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:22.963968+0800 objc_test[4652:874431] -[FFPerson lnspireCreativity]
2023-01-01 03:26:22.964015+0800 objc_test[4652:874431] class: 0x1002f10e0
2023-01-01 03:26:22.964067+0800 objc_test[4652:874431] _bucketsAndMaybeMask: 0x3000280ba0040, mask: 3
2023-01-01 03:26:22.964121+0800 objc_test[4652:874431] 4-4
2023-01-01 03:26:22.964217+0800 objc_test[4652:874431] enjoyLift-0x925c6081002e950c
2023-01-01 03:26:22.964329+0800 objc_test[4652:874431] likeFlower-0xbd2e4f81002e9494
2023-01-01 03:26:22.964441+0800 objc_test[4652:874431] likeStudy-0x5015af01002e94d0
2023-01-01 03:26:22.964541+0800 objc_test[4652:874431] lnspireCreativity-0xc8031381002e9548
2023-01-01 03:26:31.091214+0800 objc_test[4652:874431] -[FFPerson likeGirls]
2023-01-01 03:26:31.091390+0800 objc_test[4652:874431] class: 0x1002f10e0
2023-01-01 03:26:31.091427+0800 objc_test[4652:874431] _bucketsAndMaybeMask: 0x70002830a0080, mask: 7
2023-01-01 03:26:31.091457+0800 objc_test[4652:874431] 1-8
2023-01-01 03:26:31.091485+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:31.091553+0800 objc_test[4652:874431] likeGirls-0x56e7181002e941c
2023-01-01 03:26:31.091587+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:31.091612+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:31.091637+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:31.091709+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:31.091754+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:31.091814+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:31.091904+0800 objc_test[4652:874431] -[FFPerson likeFoods]
2023-01-01 03:26:31.091955+0800 objc_test[4652:874431] class: 0x1002f10e0
2023-01-01 03:26:31.092070+0800 objc_test[4652:874431] _bucketsAndMaybeMask: 0x70002830a0080, mask: 7
2023-01-01 03:26:31.092130+0800 objc_test[4652:874431] 2-8
2023-01-01 03:26:31.092189+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:31.092295+0800 objc_test[4652:874431] likeGirls-0x56e7181002e941c
2023-01-01 03:26:31.092431+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:31.092498+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:31.092594+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:31.092779+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:31.092815+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:31.092858+0800 objc_test[4652:874431] likeFoods-0xb4368481002e9458
2023-01-01 03:26:31.092889+0800 objc_test[4652:874431] -[FFPerson likeFlower]
2023-01-01 03:26:31.092960+0800 objc_test[4652:874431] class: 0x1002f10e0
2023-01-01 03:26:31.093032+0800 objc_test[4652:874431] _bucketsAndMaybeMask: 0x70002830a0080, mask: 7
2023-01-01 03:26:31.093079+0800 objc_test[4652:874431] 3-8
2023-01-01 03:26:31.093126+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:31.093232+0800 objc_test[4652:874431] likeGirls-0x56e7181002e941c
2023-01-01 03:26:31.093267+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:31.093339+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:31.093444+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:31.093599+0800 objc_test[4652:874431] likeFlower-0x2c78f181002e9494
2023-01-01 03:26:31.093634+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:31.093673+0800 objc_test[4652:874431] likeFoods-0xb4368481002e9458
2023-01-01 03:26:31.093707+0800 objc_test[4652:874431] -[FFPerson likeStudy]
2023-01-01 03:26:31.093748+0800 objc_test[4652:874431] class: 0x1002f10e0
2023-01-01 03:26:31.093827+0800 objc_test[4652:874431] _bucketsAndMaybeMask: 0x70002830a0080, mask: 7
2023-01-01 03:26:31.093907+0800 objc_test[4652:874431] 4-8
2023-01-01 03:26:31.094017+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:31.094114+0800 objc_test[4652:874431] likeGirls-0x56e7181002e941c
2023-01-01 03:26:31.094216+0800 objc_test[4652:874431] likeStudy-0x76095081002e94d0
2023-01-01 03:26:31.094259+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:31.094310+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:31.094409+0800 objc_test[4652:874431] likeFlower-0x2c78f181002e9494
2023-01-01 03:26:31.094441+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:31.094494+0800 objc_test[4652:874431] likeFoods-0xb4368481002e9458
2023-01-01 03:26:31.094542+0800 objc_test[4652:874431] -[FFPerson enjoyLift]
2023-01-01 03:26:31.094587+0800 objc_test[4652:874431] class: 0x1002f10e0
2023-01-01 03:26:31.094640+0800 objc_test[4652:874431] _bucketsAndMaybeMask: 0x70002830a0080, mask: 7
2023-01-01 03:26:31.094714+0800 objc_test[4652:874431] 5-8
2023-01-01 03:26:31.094794+0800 objc_test[4652:874431] enjoyLift-0xa35b7c01002e950c
2023-01-01 03:26:31.094846+0800 objc_test[4652:874431] likeGirls-0x56e7181002e941c
2023-01-01 03:26:31.094886+0800 objc_test[4652:874431] likeStudy-0x76095081002e94d0
2023-01-01 03:26:31.094967+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:31.095012+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:31.095101+0800 objc_test[4652:874431] likeFlower-0x2c78f181002e9494
2023-01-01 03:26:31.095156+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:31.095215+0800 objc_test[4652:874431] likeFoods-0xb4368481002e9458
2023-01-01 03:26:31.095307+0800 objc_test[4652:874431] -[FFPerson lnspireCreativity]
2023-01-01 03:26:31.095394+0800 objc_test[4652:874431] class: 0x1002f10e0
2023-01-01 03:26:31.095483+0800 objc_test[4652:874431] _bucketsAndMaybeMask: 0x70002830a0080, mask: 7
2023-01-01 03:26:31.095592+0800 objc_test[4652:874431] 6-8
2023-01-01 03:26:31.095690+0800 objc_test[4652:874431] enjoyLift-0xa35b7c01002e950c
2023-01-01 03:26:31.095761+0800 objc_test[4652:874431] likeGirls-0x56e7181002e941c
2023-01-01 03:26:31.095834+0800 objc_test[4652:874431] likeStudy-0x76095081002e94d0
2023-01-01 03:26:31.095912+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:31.095993+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:31.096094+0800 objc_test[4652:874431] likeFlower-0x2c78f181002e9494
2023-01-01 03:26:31.096194+0800 objc_test[4652:874431] lnspireCreativity-0x371a8f01002e9548
2023-01-01 03:26:31.096235+0800 objc_test[4652:874431] likeFoods-0xb4368481002e9458
2023-01-01 03:26:31.096284+0800 objc_test[4652:874431] -[FFPerson likePlay]
2023-01-01 03:26:31.096388+0800 objc_test[4652:874431] class: 0x1002f10e0
2023-01-01 03:26:31.096431+0800 objc_test[4652:874431] _bucketsAndMaybeMask: 0x70002830a0080, mask: 7
2023-01-01 03:26:31.096478+0800 objc_test[4652:874431] 7-8
2023-01-01 03:26:31.096542+0800 objc_test[4652:874431] enjoyLift-0xa35b7c01002e950c
2023-01-01 03:26:31.096628+0800 objc_test[4652:874431] likeGirls-0x56e7181002e941c
2023-01-01 03:26:31.096724+0800 objc_test[4652:874431] likeStudy-0x76095081002e94d0
2023-01-01 03:26:31.096766+0800 objc_test[4652:874431] likePlay-0x425e3e81002e9584
2023-01-01 03:26:31.096829+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:31.096903+0800 objc_test[4652:874431] likeFlower-0x2c78f181002e9494
2023-01-01 03:26:31.096997+0800 objc_test[4652:874431] lnspireCreativity-0x371a8f01002e9548
2023-01-01 03:26:31.097087+0800 objc_test[4652:874431] likeFoods-0xb4368481002e9458
2023-01-01 03:26:31.097157+0800 objc_test[4652:874431] -[FFPerson likeSleep]
2023-01-01 03:26:31.097239+0800 objc_test[4652:874431] class: 0x1002f10e0
2023-01-01 03:26:31.097320+0800 objc_test[4652:874431] _bucketsAndMaybeMask: 0x70002830a0080, mask: 7
2023-01-01 03:26:31.097386+0800 objc_test[4652:874431] 8-8
2023-01-01 03:26:31.097511+0800 objc_test[4652:874431] enjoyLift-0xa35b7c01002e950c
2023-01-01 03:26:31.097587+0800 objc_test[4652:874431] likeGirls-0x56e7181002e941c
2023-01-01 03:26:31.097634+0800 objc_test[4652:874431] likeStudy-0x76095081002e94d0
2023-01-01 03:26:31.097705+0800 objc_test[4652:874431] likePlay-0x425e3e81002e9584
2023-01-01 03:26:31.097813+0800 objc_test[4652:874431] likeSleep-0x2273e801002e95c0
2023-01-01 03:26:31.097905+0800 objc_test[4652:874431] likeFlower-0x2c78f181002e9494
2023-01-01 03:26:31.098022+0800 objc_test[4652:874431] lnspireCreativity-0x371a8f01002e9548
2023-01-01 03:26:31.098108+0800 objc_test[4652:874431] likeFoods-0xb4368481002e9458
2023-01-01 03:26:44.559761+0800 objc_test[4652:874431] -[FFPerson likeRead]
2023-01-01 03:26:44.559909+0800 objc_test[4652:874431] class: 0x1002f10e0
2023-01-01 03:26:44.559946+0800 objc_test[4652:874431] _bucketsAndMaybeMask: 0xf0002822a4100, mask: 15
2023-01-01 03:26:44.559976+0800 objc_test[4652:874431] 1-16
2023-01-01 03:26:44.560006+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:44.560041+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:44.560067+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:44.560093+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:44.560184+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:44.560252+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:44.560366+0800 objc_test[4652:874431] likeRead-0x4216ad81002e95fc
2023-01-01 03:26:44.560446+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:44.560475+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:44.560502+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:44.560528+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:44.560618+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:44.560679+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:44.560708+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:44.560768+0800 objc_test[4652:874431] (null)-0x0
2023-01-01 03:26:44.560866+0800 objc_test[4652:874431] (null)-0x0
INIT_CACHE_SIZE_LOG2
#if CACHE_END_MARKER || (__arm64__ && !__LP64__)
    INIT_CACHE_SIZE_LOG2 = 2,
#else
    INIT_CACHE_SIZE_LOG2 = 1,
#endif


cache_fill_ratio
#if __arm__  ||  __x86_64__  ||  __i386__

#define CACHE_END_MARKER 1

static inline mask_t cache_fill_ratio(mask_t capacity) {
    return capacity * 3 / 4;
}

#elif __arm64__ && !__LP64__

#define CACHE_END_MARKER 0

static inline mask_t cache_fill_ratio(mask_t capacity) {
    return capacity * 3 / 4;
}

#elif __arm64__ && __LP64__

#define CACHE_END_MARKER 0

static inline mask_t cache_fill_ratio(mask_t capacity) {
    return capacity * 7 / 8;
}

#define CACHE_ALLOW_FULL_UTILIZATION 1

#else
#error unknown architecture
#endif
  • 结论:
  1. iPhoneXs,初始缓存容量为2(INIT_CACHE_SIZE = (1 << 1)

  2. 真机扩容按照大于7/8的规则,2倍扩容,所以扩容规律2-> 4 -> 8 -> 16 -> 2^n

  3. x86_64架构初始容量为4(INIT_CACHE_SIZE = (1 << 2)

  4. 模拟器扩容按照大于等于3/4的规则,2倍扩容,扩容规律4 -> 8 -> 16 -> 2^(n+1)