Runtime06类里面的方法缓存原理

在这里插入图片描述
每一个类、每一个元类都有一个cache_t对象,cache_t对象是一个结构体,里面有3个成员变量:①存储了你之前调用过的某个类对象或者实例对象的方法(你调用过的类方法会存储在元类的cache_t对象里面,而你调用过的实例方法会存储在类的cache_t对象里面)的哈希表(下图的struct bucket_t *_buckets),该哈希表只是通过一个数组实现的,解决哈希冲突的方法是线性探测法,而不是拉链法(看接下来的bucket_t * cache_t::find(cache_key_t k, id receiver)可知该哈希表解决哈希冲突的方法是线性探测法);②哈希表的长度 - 1(下图的mask_t _mask);③已经缓存的方法数量(下图的mask_t _occupied)。其中,mask_t的定义为typedef uint32_t mask_t;
在这里插入图片描述
在看从方法缓存列表中查找目标方法之前,先看一下哈希表的实现的几种方式,因为上图的find()函数使用的只是哈希表的其中一种实现方式(线性取值法)。
下图是哈希表的实现原理简介,并以线性取值法(解决哈希冲突的一种方式)为例。
在这里插入图片描述

bucket_t * cache_t::find(cache_key_t k, id receiver)的源码如下:
在这里插入图片描述

用一个demo验证一个方法调用后会被缓存到方法缓存列表中

demo的目录结构如下:
在这里插入图片描述

main.mm(注意:要把main.m改名为main.mm,因为.m文件不支持c++代码)文件代码如下:

#import <Foundation/Foundation.h>
#import "MyClassInfo.h"
#import "Person.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        //debug调试Person类的缓存方法列表的个数的变化
        Person *person = [Person new];
        my_objc_class *personClass = (__bridge my_objc_class*)[Person class];
        [person test];
        //打印person对象的类的方法缓存列表里面缓存的所有方法
        cache_t cache = personClass->cache;
        bucket_t *buckets = cache._buckets;
        for (int i = 0; i <= cache._mask; i++) {
            bucket_t bucket = buckets[i];
            NSLog(@"SEL = %s,IMP = %p", bucket._key, bucket._imp);
        }
        NSLog(@"-------");
    }
    return 0;
}

MyClassInfo.h文件代码如下:

#import <Foundation/Foundation.h>

#ifndef MyClassInfo_h
#define MyClassInfo_h

# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL
# elif __x86_64__
#   define ISA_MASK        0x00007ffffffffff8ULL
# endif

#if __LP64__
typedef uint32_t mask_t;
#else
typedef uint16_t mask_t;
#endif
typedef uintptr_t cache_key_t;

#if __arm__  ||  __x86_64__  ||  __i386__
// objc_msgSend has few registers available.
// Cache scan increments and wraps at special end-marking bucket.
#define CACHE_END_MARKER 1
static inline mask_t cache_next(mask_t i, mask_t mask) {
    return (i+1) & mask;
}

#elif __arm64__
// objc_msgSend has lots of registers available.
// Cache scan decrements. No end marker needed.
#define CACHE_END_MARKER 0
static inline mask_t cache_next(mask_t i, mask_t mask) {
    return i ? i-1 : mask;
}

#else
#error unknown architecture
#endif

struct bucket_t {
    cache_key_t _key;
    IMP _imp;
};

struct cache_t {
    bucket_t *_buckets;
    mask_t _mask;
    mask_t _occupied;
    
    IMP imp(SEL selector)
    {
        mask_t begin = _mask & (long long)selector;
        mask_t i = begin;
        do {
            if (_buckets[i]._key == 0  ||  _buckets[i]._key == (long long)selector) {
                return _buckets[i]._imp;
            }
        } while ((i = cache_next(i, _mask)) != begin);
        return NULL;
    }
};

struct entsize_list_tt {
    uint32_t entsizeAndFlags;
    uint32_t count;
};

struct method_t {
    SEL name;
    const char *types;
    IMP imp;
};

struct method_list_t : entsize_list_tt {
    method_t first;
};

struct ivar_t {
    int32_t *offset;
    const char *name;
    const char *type;
    uint32_t alignment_raw;
    uint32_t size;
};

struct ivar_list_t : entsize_list_tt {
    ivar_t first;
};

struct property_t {
    const char *name;
    const char *attributes;
};

struct property_list_t : entsize_list_tt {
    property_t first;
};

struct chained_property_list {
    chained_property_list *next;
    uint32_t count;
    property_t list[0];
};

typedef uintptr_t protocol_ref_t;
struct protocol_list_t {
    uintptr_t count;
    protocol_ref_t list[0];
};

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;  // instance对象占用的内存空间
#ifdef __LP64__
    uint32_t reserved;
#endif
    const uint8_t * ivarLayout;
    const char * name;  // 类名
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;  // 成员变量列表
    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;
};

struct class_rw_t {
    uint32_t flags;
    uint32_t version;
    const class_ro_t *ro;
    method_list_t * methods;    // 方法列表
    property_list_t *properties;    // 属性列表
    const protocol_list_t * protocols;  // 协议列表
    Class firstSubclass;
    Class nextSiblingClass;
    char *demangledName;
};

#define FAST_DATA_MASK          0x00007ffffffffff8UL
struct class_data_bits_t {
    uintptr_t bits;
public:
    class_rw_t* data() {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }
};

/* OC对象 */
struct my_objc_object {
    void *isa;
};

/* 类对象 */
struct my_objc_class : my_objc_object {
    Class superclass;
    cache_t cache;
    class_data_bits_t bits;
public:
    class_rw_t* data() {
        return bits.data();
    }
    my_objc_class* metaClass() {
        return (my_objc_class *)((long long)isa & ISA_MASK);
    }
};

#endif /* MyClassInfo_h */

Person类的代码如下:

#import <Foundation/Foundation.h>
@interface Person : NSObject
-(void)test;
@end

//---------分割线,分隔一个类的.h文件和.m文件-------

#import "Person.h"
@implementation Person

-(void)test {
    NSLog(@"%s", __func__);
}

@end

运行结果一:
在这里插入图片描述
运行结果二
在这里插入图片描述
运行结果三
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值