在研究 runtime 时能遇到许多的类和结构体, 因为不可能每篇文章碰到就介绍一遍, 所以在这里统一把这些碰到的类和结构体介绍一下
SideTable 这个类的作用是存放引用计数表和弱引用表, 并使用自旋锁来防止操作表结构时可能的竞态条件 定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 struct SideTable { spinlock_t slock; RefcountMap refcnts; weak_table_t weak_table; SideTable() { memset(&weak_table, 0, sizeof(weak_table)); } ~SideTable() { _objc_fatal("Do not delete SideTable."); } void lock() { slock.lock(); } void unlock() { slock.unlock(); } void forceReset() { slock.forceReset(); } // Address-ordered lock discipline for a pair of side tables. template<HaveOld, HaveNew> static void lockTwo(SideTable *lock1, SideTable *lock2); template<HaveOld, HaveNew> static void unlockTwo(SideTable *lock1, SideTable *lock2); };
有下面几个成员变量
spinlock_t slock: 自旋锁
RefcountMap refcnts: 额外引用计数存储表
weak_table_t weak_table: 弱引用表, 用来存储弱引用者(weak 修饰的变量)
其中RefcountMap
是类型objc::DenseMap<DisguisedPtr<objc_object>,size_t,RefcountMapValuePurgeable>
的别名
DisguisedPtr 定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 template <typename T> class DisguisedPtr { uintptr_t value; static uintptr_t disguise(T* ptr) { return -(uintptr_t)ptr; } static T* undisguise(uintptr_t val) { return (T*)-val; } public: DisguisedPtr() { } DisguisedPtr(T* ptr) : value(disguise(ptr)) { } DisguisedPtr(const DisguisedPtr<T>& ptr) : value(ptr.value) { } DisguisedPtr<T>& operator = (T* rhs) { value = disguise(rhs); return *this; } DisguisedPtr<T>& operator = (const DisguisedPtr<T>& rhs) { value = rhs.value; return *this; } operator T* () const { return undisguise(value); } T* operator -> () const { return undisguise(value); } T& operator * () const { return *undisguise(value); } T& operator [] (size_t i) const { return undisguise(value)[i]; } // pointer arithmetic operators omitted // because we don't currently use them anywhere };
这个类的作用是伪装指针, 避免引用计数表和弱引用表里面保存的指针被内存泄漏工具leaks
当做是内存泄漏.
它的成员变量value
存储的是经过函数disguise()
处理后的指针.
你可以像操作指针一样操作DisguisedPtr
实例. 为了实现这个功能, DisguisedPtr
重载了许多的操作符, 例如->
, *
, =
, ==
, []
. 不了解的同学可以自己查资料, 关键字 运算符重载operator
DisguisedPtr
一般用来充当 Key, 保存在结构体中
RefcountMapValuePurgeable 定义如下:
1 2 3 4 5 struct RefcountMapValuePurgeable { static inline bool isPurgeable(size_t x) { return x == 0; } };
只有一个内联函数RefcountMapValuePurgeable
, 用来判断这个值需要是否析构释放内存
DenseMapInfo 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 template<typename T> struct DenseMapInfo<DisguisedPtr<T>> { static inline DisguisedPtr<T> getEmptyKey() { return DisguisedPtr<T>((T*)(uintptr_t)-1); } static inline DisguisedPtr<T> getTombstoneKey() { return DisguisedPtr<T>((T*)(uintptr_t)-2); } static unsigned getHashValue(const T *PtrVal) { return ptr_hash((uintptr_t)PtrVal); } static bool isEqual(const DisguisedPtr<T> &LHS, const DisguisedPtr<T> &RHS) { return LHS == RHS; } };
它有两个内联函数:
getEmptyKey(): 生成 empty 的标记
getTombstoneKey(): 生成墓碑标记(即代表之前被人使用过但现在已经没人用了)
有两个静态函数:
getHashValue(): 根据指针返回一个哈希值
isEqual(): 判断两个参数是否相等
DenseMapInfo
常常跟DisguisedPtr
一起使用. 它的两个内联函数用来标记存储数据的DenseMapPair
实例 bucket 的状态是否是 empty 或者 tombstoneKey. 它的getHashValue
函数用来生成一个哈希值来确定 bucket 的序号
detail::DenseMapPair 部分定义如下:
1 2 3 4 5 6 7 8 9 10 template <typename KeyT, typename ValueT> struct DenseMapPair : public std::pair<KeyT, ValueT> { // FIXME: Switch to inheriting constructors when we drop support for older // clang versions. // NOTE: This default constructor is declared with '{}' rather than // '= default' to work around a separate bug in clang-3.8. This can // also go when we switch to inheriting constructors. DenseMapPair() {} }
它的父类std::pair
是一个结构体模板, 它将两个数据组合成一个数据, 类似于我们经常用的字典.DenseMapPair
被用来在引用计数表中保存引用计数. 其中 key 的类型为DisguisedPtr
DenseMap 部分定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 template <typename KeyT, typename ValueT, typename ValueInfoT = DenseMapValueInfo<ValueT>, typename KeyInfoT = DenseMapInfo<KeyT>, typename BucketT = detail::DenseMapPair<KeyT, ValueT>> class DenseMap : public DenseMapBase<DenseMap<KeyT, ValueT, ValueInfoT, KeyInfoT, BucketT>, KeyT, ValueT, ValueInfoT, KeyInfoT, BucketT> { friend class DenseMapBase<DenseMap, KeyT, ValueT, ValueInfoT, KeyInfoT, BucketT>; // Lift some types from the dependent base class into this class for // simplicity of referring to them. using BaseT = DenseMapBase<DenseMap, KeyT, ValueT, ValueInfoT, KeyInfoT, BucketT>; BucketT *Buckets; unsigned NumEntries; unsigned NumTombstones; unsigned NumBuckets; public: /// Create a DenseMap wth an optional \p InitialReserve that guarantee that /// this number of elements can be inserted in the map without grow() explicit DenseMap(unsigned InitialReserve = 0) { init(InitialReserve); } }
这个类就是之前提到的引用计数表, 它的成员里面有一个存储数组, 用来保存引用计数. 数组的元素类型为 detail::DenseMapPair
如果一个对象的引用计数曾经溢出保存到表中, 当对象被销毁时, 会将表中对象使用过的存储器 bucket 标记为墓碑状态
1 friend class DenseMapBase<DenseMap, KeyT, ValueT, ValueInfoT, KeyInfoT, BucketT>;
用于声明一个友元类, 这样DenseMapBase
就能访问DenseMap
里面的私有属性和私有方法了
1 using BaseT = DenseMapBase<DenseMap, KeyT, ValueT, ValueInfoT, KeyInfoT, BucketT>;
为DenseMapBase<DenseMap, KeyT, ValueT, ValueInfoT, KeyInfoT, BucketT>
添加一个类型别名
1 explicit DenseMap(unsigned InitialReserve = 0) { init(InitialReserve); }
上面那行代码的作用是显示的声明一个构造方式, 这样这类就不能隐式转换了
成员变量如下:
Buckets: 一个 bucket 数组, 用于保存数据. 可扩容
NumEntries: Buckets 数组中已经被使用的数目
NumTombstones: Buckets 数组中 tombstone 的数目
NumBuckets: Buckets 数组的数目
weak_table_t 1 2 3 4 5 6 struct weak_table_t { weak_entry_t *weak_entries; size_t num_entries; uintptr_t mask; uintptr_t max_hash_displacement; };
这个结构体用来存储弱引用条目, 弱引用条目里面保存着对象以及它的弱引用者们. 当数组数量超过 1024 且被使用的数量占比小于 1/16 时, 数组长度会缩小为原来的 1/8
它有 4 个成员变量:
weak_entry_t *weak_entries: weak_entry_t 类型的数组. 弱引用条目, 用来保存弱引用者(被 weak 修饰的指针)
size_t num_entries: 已经被使用的条目数量
uintptr_t mask: 条目数组的数量
uintptr_t max_hash_displacement: 用来记录数组中被使用条目的 index 的最大值
weak_entry_t 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 struct weak_entry_t { DisguisedPtr<objc_object> referent; union { struct { weak_referrer_t *referrers; uintptr_t out_of_line_ness : 2; uintptr_t num_refs : PTR_MINUS_2; uintptr_t mask; uintptr_t max_hash_displacement; }; struct { // out_of_line_ness field is low bits of inline_referrers[1] weak_referrer_t inline_referrers[WEAK_INLINE_COUNT]; }; }; bool out_of_line() { return (out_of_line_ness == REFERRERS_OUT_OF_LINE); } weak_entry_t& operator=(const weak_entry_t& other) { memcpy(this, &other, sizeof(other)); return *this; } weak_entry_t(objc_object *newReferent, objc_object **newReferrer) : referent(newReferent) { inline_referrers[0] = newReferrer; for (int i = 1; i < WEAK_INLINE_COUNT; i++) { inline_referrers[i] = nil; } } };
这个结构体用来保存引用对象以及它的弱引用者, 属于一对多的关系 当弱引用比较少的时候会将弱引用者保存在结构体里面, 当弱引用者数量超过 4 时会保存到外部的数组中
它有两个成员变量, 一个是DisguisedPtr<objc_object> referent;
, 另一个是联合体 union. referent 表示引用对象, 当一个弱引用者引用了一个新的对象, 那么我们需要从弱引用条目中(weak_entry_t)移除该弱引用者
weak_referrer_t
是 DisguisedPtr<objc_object *>
类型的别名
第二个成员变量 union 里面有两个结构体, 我这里称呼它们为 s1, s2. 因为是 union, 所以 s1 里面的 out_of_line_ness 跟 s2 里面的 inline_referrers 的第二个元素的低 2 个 bit 是重合的. 数组 inline_referrers 的元素是 weak_referrer_t 类型. 在 arm64 架构下, 指针 8 字节对齐, 意味着指针低 3 位肯定都是 0, 经过DisguisedPtr
的伪装后, 它的低 2 位都是 1, 也就是 s1 的 out_of_line_ness 的值为 0b11. 这个特性用来标记是否使用内部数组来保存弱引用者, 当使用外部数组时, 内部数组被清空, 将 out_of_line_ness 赋值为 0b10 来表示使用外部数组
当使用内部数组时
s1 的成员变量均没有意义
使用 s2 的 inline_referrers 数组来保存弱引用者.
当使用外部数组时
s1 的成员变量referrers
是指向外部数组的指针; out_of_line_ness
为常量 2, 表示使用了外部数组; mask
表示外部数组的长度 - 1; num_refs
表示外部数组中被使用的数量; max_hash_displacement
表示哈希最大偏移量
此时 s2 的成员变量 inline_referrers 被清空