NSObject底层实现

2019-01-21  本文已影响26人  昵称是乱起的
NSObject的实现(OC2.0)
@interface NSObject <NSObject> {
    Class isa  OBJC_ISA_AVAILABILITY;
}
typedef struct objc_class *Class;
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

    class_rw_t *data() { 
        return bits.data();
    }
    void setData(class_rw_t *newData) {
        bits.setData(newData);
    }
。。。省略几十个方法
}
struct objc_object {
    isa_t isa;
};
//总的来说objc_class结构体主要有以下变量
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
}
union isa_t 
{
    Class cls;
    uintptr_t bits;
# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL
#   define ISA_MAGIC_MASK  0x000003f000000001ULL
#   define ISA_MAGIC_VALUE 0x000001a000000001ULL
    struct {
        uintptr_t nonpointer        : 1;
        uintptr_t has_assoc         : 1;
        uintptr_t has_cxx_dtor      : 1;
        uintptr_t shiftcls          : 33; // MACH_VM_MAX_ADDRESS 0x1000000000
        uintptr_t magic             : 6;
        uintptr_t weakly_referenced : 1;
        uintptr_t deallocating      : 1;
        uintptr_t has_sidetable_rc  : 1;
        uintptr_t extra_rc          : 19;
#       define RC_ONE   (1ULL<<45)
#       define RC_HALF  (1ULL<<18)
    };
}
对于实例对象、类对象、元类对象 isa和superclass的关系还得看这张图,具体的isa过几天再总结
isa.png
cache_t cache; 方法缓存,用散列表(哈希表)来缓存曾经调用过的方法,可以提高方法的查找速度
struct cache_t {
    struct bucket_t *_buckets;//散列表数组
    mask_t _mask;//散列表的长度-1
    mask_t _occupied;//已缓存方法数量
}
struct bucket_t {
    cache_key_t _key;//用SEL作为key
    IMP _imp;//函数的内存地址
}
//缓存查找的关键代码
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);
    mask_t i = begin;
    do {
        if (b[i].key() == 0  ||  b[i].key() == k) {
            return &b[i];
        }
    } 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);
}
static inline mask_t cache_hash(cache_key_t key, mask_t mask) 
{
    return (mask_t)(key & mask);
}
// __arm64__下是这个方法
static inline mask_t cache_next(mask_t i, mask_t mask) {
    return i ? i-1 : mask;
}
上面缓存查找的思路

用k(就是SEL方法名)与mask做一次cache_hash,得到一个begin索引,然后根据索引去buckets数组中取出bucket_t里面的key跟外面的k做比较,如果一样就返回这个bucket_t,如果不一样cache_next,arm64下就是begin索引-1,然后从buckets里面取出继续做比较,直到begin==0,然后又从buckets数组的count重新找,最多遍历一遍数组,如果找不到就去方法列表查找去了

class_data_bits_t bits里面存着一些类的信息,比如class_rw_t,class_ro_t
class_rw_t* data() {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
  }
struct class_rw_t {
    const class_ro_t *ro;
    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;
}
//class_ro_t里面的baseMethodList、baseProtocols、ivars、baseProperties是一维数组,是只读的,包含了类的初始内容
struct class_ro_t {
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;
};
struct method_t {
    SEL name;//函数名
    const char *types;//函数返回值、参数编码的字符串
    IMP imp;//函数地址
};

class_rw_t 的methods、properties、protocols里面是二位数组,看methods里面就可以,methods里面是method_list_t数组,method_list_t里面是method_list的结构体


image.png
上一篇下一篇

猜你喜欢

热点阅读