应用源码学习
- weak
Runtime 源码分析,带你了解 OC 实现过程。其中参考了大量的大神的代码以及文献,里面也有个人的见解,欢迎拍砖,欢迎交流。
- /// weak属性
- @interface XX : XX
- @property(nonatomic,weak) Type* weakPtr;
- @end
- /// 代码块中使用
- {
- /// 使用__weak
- __weak Type* weakPtr = [[SomeObject alloc] init];
- }
根据调试信息,发现两者的区别是:
```/**
- id objc_storeWeak(id *location, id newObj)方法
``` Objective-C/**
- id objc_initWeak(id *location, id newObj)
- template <HaveOld haveOld, HaveNew haveNew,
- CrashIfDeallocating crashIfDeallocating>
- static id
- storeWeak(id *location, objc_object *newObj)
- {
- ///略去,下面会进行分析
- ...
- return (id)newObj;
- }
所以重点就在
这个方法中,let's do it
- storeWeak
源码的如下:
- storeWeak
- template <HaveOld haveOld, HaveNew haveNew,
- CrashIfDeallocating crashIfDeallocating>
- static id storeWeak(id *location, objc_object *newObj)
- {
- assert(haveOld || haveNew);
- if (!haveNew) assert(newObj == nil);
- Class previouslyInitializedClass = nil;
- id oldObj;
- SideTable *oldTable;
- SideTable *newTable;
- // Acquire locks for old and new values.
- // Order by lock address to prevent lock ordering problems.
- // Retry if the old value changes underneath us.
- retry:
- if (haveOld) {
- oldObj = *location;
- oldTable = &SideTables()[oldObj];
- } else {
- oldTable = nil;
- }
- if (haveNew) {
- newTable = &SideTables()[newObj];
- } else {
- newTable = nil;
- }
- SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable);
- if (haveOld && *location != oldObj) {
- SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
- goto retry;
- }
- // Prevent a deadlock between the weak reference machinery
- // and the +initialize machinery by ensuring that no
- // weakly-referenced object has an un-+initialized isa.
- /// 注释大意是通过下面操作,保证所有的弱引用对象的isa都被初始化,这样可以防止死锁,PS,这里我不是太明白,求指教
- if (haveNew && newObj) {
- /// 下面的操作是初始化isa
- Class cls = newObj->getIsa();
- if (cls != previouslyInitializedClass &&
- !((objc_class *)cls)->isInitialized())
- {
- SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
- _class_initialize(_class_getNonMetaClass(cls, (id)newObj));
- // If this class is finished with +initialize then we're good.
- // If this class is still running +initialize on this thread
- // (i.e. +initialize called storeWeak on an instance of itself)
- // then we may proceed but it will appear initializing and
- // not yet initialized to the check above.
- // Instead set previouslyInitializedClass to recognize it on retry.
- previouslyInitializedClass = cls;
- goto retry;
- }
- }
- // Clean up old value, if any.
- if (haveOld) {
- weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
- }
- // Assign new value, if any.
- if (haveNew) {
- newObj = (objc_object *)
- weak_register_no_lock(&newTable->weak_table, (id)newObj, location,
- crashIfDeallocating);
- // weak_register_no_lock returns nil if weak store should be rejected
- // Set is-weakly-referenced bit in refcount table.
- if (newObj && !newObj->isTaggedPointer()) {
- newObj->setWeaklyReferenced_nolock();
- }
- // Do not set *location anywhere else. That would introduce a race.
- *location = (id)newObj;
- }
- else {
- // No new value. The storage is not changed.
- }
- SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
- return (id)newObj;
- }
PS: 初始化 ISA 那部分为何能阻止死锁,我没有看懂 该函数流程如下:
重点来了:
- /// SideTables
- oldTable = &SideTables()[oldObj];
- newTable = &SideTables()[newObj];
- /// taggedPointer是什么鬼
- isTaggedPointer
- /// 注册弱引用
- weak_register_no_lock(&newTable->weak_table, (id)newObj, location,crashIfDeallocating);
- /// 消除弱引用
- weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
是一个结构体,定义如下
- SideTable
- 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.");
- }
- ///锁
- ....
- };
是存放引用关系的,对象通过 Hash值操作,在
- SideTable
中寻找与之对应的
- SideTableBuf
,
- SideTable
初始化过程如下:
- SideTableBuf
- alignas(StripedMap<SideTable>) static uint8_t
- SideTableBuf[sizeof(StripedMap<SideTable>)];
- /// 会在Objc_init中调用该方法
- static void SideTableInit() {
- /// 这句话貌似没什么卵用,求指教
- new (SideTableBuf) StripedMap<SideTable>();
- }
- /// 寻找SideTable
- static StripedMap<SideTable>& SideTables() {
- return *reinterpret_cast<StripedMap<SideTable>*>(SideTableBuf);
- }
是一个泛型类,并重写了 [] 运算符,通过对象的地址,运算出 Hash 值,通过该 hash 值找到对象的 SideTable ``` template
- StripedMap
public: T& operator[] (const void *p) {return array[indexForPointer(p)].value; } /// 下面略去 ... }
- ### taggedPointer
- 简单的说,这是一种优化手段,即将对象的值,存入对象的地址中,这些工程师简直丧心病狂,就为了省一点内存嘛!
- ### 进入正题,看看怎么实现弱引用的
- 先看看注册的过程吧
/**
后三个参数不用解释,主要解释第一个参数,
, 定义如下
- weak_table_t
- /**
- * The global weak references table. Stores object ids as keys,
- * and weak_entry_t structs as their values.
- */
- struct weak_table_t {
- weak_entry_t *weak_entries; ///数组,用于存储引用对象集合
- size_t num_entries; /// 存储数目
- uintptr_t mask; /// 当前分配容量
- uintptr_t max_hash_displacement; /// 已使用容量
- };
没错,
就是寄存在
- weak_table_t
中
- SideTable
定义中我们重点关注
- weak_entry_t
- 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;
- }
- }
- };
是最终存放对象和引用指针的地方,
- weak_entry_t
是被引用的对象,联合体
- referent
释义如下
- union
注册引用过程中,重点关注下面代码:
- {
- weak_entry_t *entry;
- /// 查找是否已经注册过了
- if ((entry = weak_entry_for_referent(weak_table, referent))) {
- /// 加上去就可以了
- append_referrer(entry, referrer);
- }
- else {
- /// 新建一个
- weak_entry_t new_entry(referent, referrer);
- /// 调整weak_table_t 的容量大小
- weak_grow_maybe(weak_table);
- /// 插入一个
- weak_entry_insert(weak_table, &new_entry);
- }
- }
通过
的源码,可以看到新建一个
- weak_entry_t
的过程是
- weak_entry_t
,因为此时数目还很少
- inline_referrers
- static void weak_resize(weak_table_t *weak_table, size_t new_size)
- {
- size_t old_size = TABLE_SIZE(weak_table);
- weak_entry_t *old_entries = weak_table->weak_entries;
- weak_entry_t *new_entries = (weak_entry_t *)
- calloc(new_size, sizeof(weak_entry_t));
- weak_table->mask = new_size - 1;
- weak_table->weak_entries = new_entries;
- /// 重置
- weak_table->max_hash_displacement = 0;
- weak_table->num_entries = 0; // restored by weak_entry_insert below
- if (old_entries) {
- weak_entry_t *entry;
- weak_entry_t *end = old_entries + old_size;
- for (entry = old_entries; entry < end; entry++) {
- if (entry->referent) {
- weak_entry_insert(weak_table, entry);
- }
- }
- free(old_entries);
- }
- }
- // Grow the given zone's table of weak references if it is full.
- static void weak_grow_maybe(weak_table_t *weak_table)
- {
- size_t old_size = TABLE_SIZE(weak_table);
- // Grow if at least 3/4 full.
- if (weak_table->num_entries >= old_size * 3 / 4) {
- weak_resize(weak_table, old_size ? old_size*2 : 64);
- }
- }
当实际的数目大于 old_size(old_size 就是 mask 的大小 + 1),就去调整大小,同时重置 max_hash_displacement 为 0,通过 calloc 函数,动态分配 mask 个的内存,然后通过循环,将原有的
插入到新的容器中,在插入的过程中,更新 max_hash_displacement.
- weak_entry_t
插入
- weak_table_t
- weak_entry_t
- static void weak_entry_insert(weak_table_t *weak_table, weak_entry_t *new_entry)
- {
- weak_entry_t *weak_entries = weak_table->weak_entries;
- assert(weak_entries != nil);
- size_t begin = hash_pointer(new_entry->referent) & (weak_table->mask);
- size_t index = begin;
- size_t hash_displacement = 0;
- while (weak_entries[index].referent != nil) {
- index = (index+1) & weak_table->mask;
- if (index == begin) bad_weak_table(weak_entries);
- hash_displacement++;
- }
- /// 把新的加进去
- weak_entries[index] = *new_entry;
- /// 引用计数+1
- weak_table->num_entries++;
- /// 扩容前最大占位
- if (hash_displacement > weak_table->max_hash_displacement) {
- weak_table->max_hash_displacement = hash_displacement;
- }
- }
过程比较简单,也是利用 hash 处理,方便后面查找。
查找对象是通过循环遍历的方式,过程如下
- weak_table_t
- static weak_entry_t *
- weak_entry_for_referent(weak_table_t *weak_table, objc_object *referent)
- {
- assert(referent);
- weak_entry_t *weak_entries = weak_table->weak_entries;
- if (!weak_entries) return nil;
- size_t begin = hash_pointer(referent) & weak_table->mask; /// 获取hash值
- size_t index = begin;
- size_t hash_displacement = 0;
- /// 循环遍历,查找
- while (weak_table->weak_entries[index].referent != referent) {
- index = (index+1) & weak_table->mask;
- if (index == begin) bad_weak_table(weak_table->weak_entries);
- // 查找到最大的时候,结束
- hash_displacement++;
- if (hash_displacement > weak_table->max_hash_displacement) {
- return nil;
- }
- }
- return &weak_table->weak_entries[index];
- }
中加入引用
- weak_entry_t
- static void append_referrer(weak_entry_t *entry, objc_object **new_referrer)
- {
- /// 如果是数组,即个数比较少
- if (! entry->out_of_line()) {
- // Try to insert inline.
- for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
- if (entry->inline_referrers[i] == nil) {
- entry->inline_referrers[i] = new_referrer;
- return;
- }
- }
- // Couldn't insert inline. Allocate out of line.
- weak_referrer_t *new_referrers = (weak_referrer_t *)
- calloc(WEAK_INLINE_COUNT, sizeof(weak_referrer_t));
- // This constructed table is invalid, but grow_refs_and_insert
- // will fix it and rehash it.
- for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
- new_referrers[i] = entry->inline_referrers[i];
- }
- entry->referrers = new_referrers;
- entry->num_refs = WEAK_INLINE_COUNT;
- entry->out_of_line_ness = REFERRERS_OUT_OF_LINE;
- entry->mask = WEAK_INLINE_COUNT-1;
- entry->max_hash_displacement = 0;
- }
- assert(entry->out_of_line());
- if (entry->num_refs >= TABLE_SIZE(entry) * 3/4) {
- return grow_refs_and_insert(entry, new_referrer);
- }
- size_t begin = w_hash_pointer(new_referrer) & (entry->mask);
- size_t index = begin;
- size_t hash_displacement = 0;
- while (entry->referrers[index] != nil) {
- hash_displacement++;
- index = (index+1) & entry->mask;
- if (index == begin) bad_weak_table(entry);
- }
- if (hash_displacement > entry->max_hash_displacement) {
- entry->max_hash_displacement = hash_displacement;
- }
- weak_referrer_t &ref = entry->referrers[index];
- ref = new_referrer;
- entry->num_refs++;
- }
该过程同在
中插入
- weak_table_t
如出一辙,要注意的是需要判断引用的个数,当引用个数大于 WEAK_INLINE_COUNT 时,需要将原有的引用指针也移到
- weak_entry_t
中,同时更新相关计数器。 上面过程的流程如下:
- referrers
消除弱引用过程同注册大致相同,只是部分地方是相反操作,不做赘述了
来源: http://www.cnblogs.com/wws19125/p/6978563.html