以前只是看了很多博客,这次打算看一下源码,并记录下来。想到哪里就读到哪里,写到哪里。读的代码版本是:
objc runtime 680
,可以从这里下载
对象和类
首先在 objc-private.h
文件中可以看到objc_object
结构体,这就是对对象的定义
struct objc_object {
private:
isa_t isa;
}
在objc-runtime-new.h
中可以看到objc_class
结构体,这就是对类的定义,因为它继承了objc_object
,所以我把isa
变量也加入到objc_class
中来。
struct objc_class : objc_object {
isa_t isa;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
}
superclass
指向该类的父类,NSObject
的父类指向NULL
Cache
cache
主要用于方法性能优化,如果每发送一个消息都需要去方法表中去查找,当方法很多的时候,查找是很耗力的,并且当 存在继承关系的时候,一个方法的查找链可能会很长。那么对使用过的方法进行缓存,便于第二次查找,这样节省的时间也是非常可观的。
来看一下cache_t
的具体代码,在objc-runtime-new.h
的第52行可以看到
struct cache_t {
struct bucket_t *_buckets;
mask_t _mask;
mask_t _occupied;
}
-
mask
:分配用来缓存bucket
的总数 -
occupied
:表明目前实际占用的缓存bucket
的个数 -
_buckets
:一个散列表,用来方法缓存,bucket_t
类型,包含key
以及方法实现IMP
struct bucket_t {
private:
cache_key_t _key;
IMP _imp;
}
class_data_bits_t
在objc-runtime-new.h
的 817 行可以看到结构体中只有一个bits
属性来存储类信息
struct class_data_bits_t {
// Values are the FAST_ flags above.
uintptr_t bits;
}
还有objc_class
有注释说
// class_rw_t * plus custom rr/alloc flags
那我们看一下class_rw_t
到底是什么,在objc-runtime-new.h
的778行可以看到
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;
}
Objc
的类的属性、方法、以及遵循的协议都放在class_rw_t
中,下面对其中几个关键词进行分析
class_ro_t
ro
是一个指向常量的指针,存储来编译器决定了的属性、方法和遵守协议
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
#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;
method_list_t *baseMethods() const {
return baseMethodList;
}
};
Method
Method
存储了方法名、类型信息、方法实现
struct method_t {
SEL name;
const char *types;
IMP imp;
}
iVar
ivar
代表类中的实例变量
struct ivar_t {
#if __x86_64__
// *offset was originally 64-bit on some x86_64 platforms.
// We read and write only 32 bits of it.
// Some metadata provides all 64 bits. This is harmless for unsigned
// little-endian values.
// Some code uses all 64 bits. class_addIvar() over-allocates the
// offset for their benefit.
#endif
int32_t *offset;
const char *name;
const char *type;
// alignment is sometimes -1; use alignment() instead
uint32_t alignment_raw;
uint32_t size;
uint32_t alignment() const {
if (alignment_raw == ~(uint32_t)0) return 1U << WORD_SHIFT;
return 1 << alignment_raw;
}
};
说起来实例变量,就想到了对象的内存结构,为了搞清楚,也做了一个测试,Person
继承于People
,People
继承于NSObject
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [[Person alloc] init];
// 此处打断点
}
}
在注释处打断点,然后在 Console
中输入p *person
,查看输出结果,对象中会包含一个isa指针、类的实例变量,以及父类的实例变量
(lldb) p *person
(Person) $0 = {
People = {
NSObject = {
isa = Person
}
_name = nil
}
_age = nil
}
(lldb)
如果有理解错误,望留言告知