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.pngcache_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
网友评论