iOS 开发 Objective-C

iOS 底层 day12 runtime method详解

2020-09-04  本文已影响0人  望穿秋水小作坊

一、class_rw_t 数据结构

Method 相关结构体以及存贮结构

二、method_t 数据结构

method_t
1. SEL
SEL sel1 = @selector(setTest:);
SEL sel2 = sel_registerName("setTest:");
// 打印日志
(lldb) p/x sel1
(SEL) $0 = 0x00007fff3a201718 "setTest:"
(lldb) p/x sel2
(SEL) $1 = 0x00007fff3a201718 "setTest:"
2. IMP
注意图中红框位置
3. Types
部分对照表 图解 types

三、cache_t 方法缓存

struct objc_class : objc_object {
    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
}

我们之前学过,当我们调用一个实例方法时,会先从 isa 指针入手,查找类对象的方法列表,然后使用 superclass 逐级查询父类的类对象方法列表。而我们现实代码中,通常来说都有一些常用的方法,如果每次都要走上面的步骤去查找,效率就会显得比较低。所以苹果设计了方法缓存机制,让方法缓存在cache中。

图解整个过程

有如下代码结构:

1. 当我们调用 GoodStudent- (void)personTest; 方法,该方法会被缓存到哪里?
int main(int argc, const char * argv[]) {
    GoodStudent *goodStudent = [[GoodStudent alloc] init];
    mj_objc_class *goodSudentClass = (__bridge mj_objc_class *)[GoodStudent class];
    mj_objc_class *personClass = (__bridge mj_objc_class *)[Person class];
    NSLog(@"断点1");
    [goodStudent personTest];
    NSLog(@"断点2");
    return 0;
}
2. 上面提到方法缓存列表是一个散列表,如何证明,OC 内部使用的散列算法是 @selctor(funName) & mask 这种算法呢?方法一:从源码中寻找
源码中缓存列表查找过程
3. 上面提到方法缓存列表是一个散列表,如何证明,OC 内部使用的散列算法是 @selctor(funName) & mask 这种算法呢?方法二:从代码中证明
int main(int argc, const char * argv[]) {
    GoodStudent *goodStudent = [[GoodStudent alloc] init];
    mj_objc_class *goodSudentClass = (__bridge mj_objc_class *)[GoodStudent class];
    
    [goodStudent studentTest];
    [goodStudent personTest];
    
    
    int begin1 = (unsigned long)@selector(studentTest) & 3;
    int begin2 = (unsigned long)@selector(init) & 3;
    int begin3 = (unsigned long)@selector(personTest) & 3;
    
    NSLog(@"studentTest-index:%d",begin1);
    NSLog(@"init-index:%d",begin2);
    NSLog(@"personTest-index:%d",begin3);
    
    bucket_t *buckets = goodSudentClass->cache._buckets;
    
    for (int i = 0; i <= goodSudentClass->cache._mask ; i++) {
        bucket_t bucket = *(buckets+i);
        printf("索引值:%d , 方法名:%s , 方法地址:%p \n",i,bucket._key, bucket._imp);
    }
    
    NSLog(@"断点1");
    return 0;
}
 Demo[3887:171505] studentTest-index:3
 Demo[3887:171505] init-index:1
 Demo[3887:171505] personTest-index:3
索引值:0 , 方法名:(null) , 方法地址:0x0 
索引值:1 , 方法名:init , 方法地址:0x1055891ad 
索引值:2 , 方法名:personTest , 方法地址:0x104c575d0 
索引值:3 , 方法名:studentTest , 方法地址:0x104c575a0 
4. 如果方法缓存列表 满了会怎么做呢?
方法缓存散列表扩容示意图
上一篇 下一篇

猜你喜欢

热点阅读