Runtime之 Class 结构的剖析

从源码objc_runtime-new.h 中可以看到

struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;  //方法缓存         
    class_data_bits_t bits;  //用于获取具体的类信息
    class_rw_t *data() { 

        return bits.data();

    }
 }

// 我们来看 bits*data 这个方法的实现

  class_rw_t* data() {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }
// bits & FAST_DATA_MASK 返回一个class_rw_t 的结构体指针

接着我们来窥探下 class_rw_t 都存储来哪些东西?

class_rw_t里面的methodspropertiesprotocols是二维数组,是可读可写的,包含了类的初始内容、分类的内容

struct class_rw_t {
 
    uint32_t flags;
    uint32_t version;

    const class_ro_t *ro; 
    method_array_t methods;//方法列表
    property_array_t properties; //属性列表
    protocol_array_t protocols;//协议列表

    Class firstSubclass;
    Class nextSiblingClass;

    char *demangledName;
 }

//class_ro_t  如下

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize; //instance对象的size
#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;
}

 

class_ro_t里面的baseMethodListbaseProtocolsivarsbaseProperties是一维数组,是只读的,包含了类的初始内容

然后我来在来看看method_t 吧。

struct method_t {
    SEL name; //方法选择器(方法名)
    const char *types; //编码(返回值类型,参数类型)
/*
    ios提供了一个@encode的指令,可以将具体的类型变成字符串编码。
    假如有这样一个方法:
    -(int)test:(int)Age Height:(CGFloat)height;
    type 为:i24@:08i16f20
    其中i为返回值类型,24 所有参数的字节总数(id,SEL,int,float),@是id,:代表SEL,i参数int,f参数float.另外的数字代表参数 开始的字节位数
*/
    IMP imp;//方法的实现
}

SEL代表方法\函数名,一般叫做选择器,底层结构跟char *类似,想要验证也很简单。如下:

char *str = "init";
NSLog(@"%s-%@",str,NSStringFromSelector(@selector(init)));

//打印如下
2018-10-31 15:52:10.218499+0800 关联对象[3274:207551] init-init

可以通过sel_getName()NSStringFromSelector()转成字符串

不同类中相同名字的方法,所对应的方法选择器是相同的

继续往下看:Class内部结构中有个方法缓存(cache_t),用散列表(哈希表)来缓存曾经调用过的方法,可以提高方法的查找速度。(很重要)

struct cache_t {
    struct bucket_t *_buckets; //散列表 
    mask_t _mask; //散列表的长度-1
    mask_t _occupied;//已经缓存的方法数量
}
struct bucket_t {
private:
    cache_key_t _key; // SEL作为key
    IMP _imp;   //函数的内存地址,可理解为value
}
//简述下原理:对象调用方法是通过isa 是找到类对象,
然后在类对象通过方法缓存去找到对应的方法缓存列表_buckets,
一旦发现调用的SEL与缓存列表中的key 相同,就指出取出函数的地址,
通过地址直接调用函数方法。

原理如下:

0 NULL
1 NULL
2 NULL
4 bucket_t (_key=@select(test),_imp)
...... .....

假如散列表如上,对象的方法名叫test。他会拿对象方法的地址 & _mask(上面介绍过了是散列表的长度-1) 假如为4,他就会去坐标为4的散列表找方法,(直接去找坐标为4的方法列表,前面的列表位置留空)假如方法存在直接调用,假如不存在就直接缓存进去。假如两个不同的地址值 & _mask得出来的索引值相同,那会怎么做呐?来看下源码

bucket_t * cache_t::find(cache_key_t k, id receiver)
{
    assert(k != 0);

    bucket_t *b = buckets();
    mask_t m = mask();
    mask_t begin = cache_hash(k, m);
/*
static inline mask_t cache_hash(cache_key_t key, mask_t mask) 
{
    return (mask_t)(key & mask);
}
*/
    mask_t i = begin;
    do {
        if (b[i].key() == 0  ||  b[i].key() == k) {
            return &b[i];
        }
/*
cache_next 的实现
#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;
}
*/
    } while ((i = cache_next(i, m)) != begin);

    // hack
    Class cls = (Class)((uintptr_t)this - offsetof(objc_class, cache));
    cache_t::bad_cache(receiver, (SEL)k, cls);
}

发现相同之后,吧得到的下标-1 缓存到得到的下标里去。如果直接减到0的时候直接赋值mask,然后在-1 慢慢的找,直到找到空余的位置缓存。以空间换时间。大家可以自己参考objc_cache.mm文件中。

//一个类的内部结构-供参考:一个c++文件
//  XZClassInfo.h
//  TestClass


#import <Foundation/Foundation.h>

#ifndef XZClassInfo_h
#define XZClassInfo_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 mj_objc_object {
    void *isa;
};

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

#endif /* XZClassInfo_h */

用法如下:

        mj_objc_class *goodStuderntClass = (__bridge mj_objc_class *)[XZGoodStudent class];

        cache_t cache = goodStuderntClass->cache;

        bucket_t *buckets = goodStuderntClass->cache._buckets;

        bucket_t bucket =buckets [(long long)@selector(goodStudentTest) & cache._mask];

        NSLog(@"%s",bucket._key);

猜你喜欢

转载自blog.csdn.net/qq_33726122/article/details/83545205
今日推荐