类的结构
从内存加载类的变化
从内存中加载流程如图所示:
clean Memory
和 Dirty Memory
clean Memory
是加载后不会发生更改的内存。clean Memory
可以进行移除,从而节省更多空间。因为如果需要clean Memory
数据,可以从内存中去加载。Dirty Memory
是运行时会发生更改后的内存。类一经使用就变成了Dirty Memory
,因为运行时会向它写入新数据。例如创建方法缓存并从类中指向它。Dirty Memory
要比clean Memory
昂贵的多,只要进程在运行,就一直存在
从源码看类
从objc4-818.2
的源码中去查看
Class
继承objc_class
,然后我们在源码中去搜索,发现有两个:
struct objc_class // OBJC2_UNAVAILABLE
// 和
struct objc_class : objc_object
由于第一个是OBJC2_UNAVAILABLE
也就是objc2不可用
,所以我们选择分析第二个,这里objc_class
继承objc_object
,
-
- 在
bits
的注释处提到class_rw_t
,这个是什么呢,objc_class
中对它有一个存取:
- 在
class_rw_t *data() const {
return bits.data();
}
void setData(class_rw_t *newData) {
bits.setData(newData);
}
它是从bits.data()
里获取的,去看看class_rw_t
const class_ro_t *ro()
void set_ro(const class_ro_t *ro)
const method_array_t methods()
const property_array_t properties()
const protocol_array_t protocols()
// 继承自list_array_tt
class method_array_t : public list_array_tt
class property_array_t : public list_array_tt
class protocol_array_t : public list_array_tt
获取类的属性
//
class list_array_tt {
struct array_t {
};
protected:
// iterator是 迭代器方法
class iterator {
const Ptr<List> *lists;
const Ptr<List> *listsEnd;
typename List::iterator m, mEnd;
public:
iterator(const Ptr<List> *begin, const Ptr<List> *end)
: lists(begin), listsEnd(end)
{
if (begin != end) { // 从begin和end的位置都放到里面,意味着具备遍历能力
m = (*begin)->begin();
mEnd = (*begin)->end();
}
}
// 取出元素
const Element& operator * () const {
return *m;
}
Element& operator * () {
return *m;
}
...
}
...
}
// iterator 迭代器的ptr 是一个属性property_list_t *conststruct property_list_t : entsize_list_tt<property_t, property_list_t, 0> {
};
// property_list_t 继承 entsize_list_tt
struct entsize_list_tt {
...
Element& get(uint32_t i) const {
ASSERT(i < count);
return getOrEnd(i);
}
...
}
struct property_list_t : entsize_list_tt<property_t, property_list_t, 0> {
};
// property_list_t 继承 entsize_list_tt
struct entsize_list_tt {
...
Element& get(uint32_t i) const {
ASSERT(i < count);
return getOrEnd(i);
}
...
}
可以使用get来取出内容get(0)
struct property_t {
const char *name;
const char *attributes;
};
获取类的方法
- 获取bits地址
- 获取data数据
- 获取 class_rw_t 中的 方法数组
- 获取方法列表的指针数组
- 获取ptr
- 获取内存信息get(0)[由于和属性不一样]
- get(0).big
struct method_t {
struct big {
SEL name;
const char *types;
MethodListIMP imp;
};
...
}
获取类实例变量(ivar)
然后class_ro_t里面有个ivar_list_t是实例变量列表
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
#ifdef __LP64__ // 表示指针长度为64位,即地址长度以64位长度来表示
uint32_t reserved;
#endif
union {
const uint8_t * ivarLayout;
Class nonMetaclass;
};
explicit_atomic<const char *> name;
// With ptrauth, this is signed if it points to a small list, but
// may be unsigned if it points to a big list.
void *baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars;
...
}
然后就能拿到实例变量
(ivar_t) $7 = {
offset = 0x00000001000082a8
name = 0x0000000100003f3f "subject"
type = 0x0000000100003f89 "@\"NSString\""
alignment_raw = 3
size = 8
}
获取类方法
类方法存储在元类里,元类的结构和类一样,先拿到元类,然后根据前面的方法拿method
总结
- 获取
属性
:我们拿到当前类
的bits.data
,它的类型是class_rw_t
,然后去找class_rw_t
的properties
数据,最后拿到property_list_t
数组,然后根据get()
方法,传入属性位置,进而拿到属性。 - 获取
实例方法
:我们拿到class_rw_t
数据后,然后去拿里面的methods()
,然后一步步往里面拿,拿到method_t
的数据类型,再调用里面的big
方法就拿到了实例方法
。 - 获取
实例变量
:我们拿到class_rw_t
后,然后拿到里面的ro
方法,在拿到ivars
信息,然后一步步拿到entsize_list_tt
数组,再根据get()
传入位置,就拿到了实例变量
。 - 获取
类方法
:我们先拿到元类
,然后根据总结2
的步骤,最后就拿到了类方法
。