runtime源码探究(一) weak的实现
来源:互联网 发布:大数据都包括什么 编辑:程序博客网 时间:2024/05/18 01:19
weak指针的建立
weak修饰对象不增加其引用计数,apple通过一个hash表来实现对象的弱引用。
在Xcode下编写如下代码:
__weak obj1 = obj;
编译器编译之后变成类似如下的代码:
objc_initWeak(&obj1,obj);
翻开runtime源码,NSObject.mm,找到objc_initWeak函数,实现如下:
* @param location Address of __weak ptr. * @param newObj Object ptr. */idobjc_initWeak(id *location, id newObj){ if (!newObj) { *location = nil; return nil; } return storeWeak<false/*old*/, true/*new*/, true/*crash*/>(location, (objc_object*)newObj);}
apple对参数已有说明,location是weak指针的地址,newObj是被指向的对象的地址。接下来寻找storeWeak函数,先看apple对storeWeak函数的注释:
// Update a weak variable.
// If HaveOld is true, the variable has an existing value
// that needs to be cleaned up. This value might be nil.
// If HaveNew is true, there is a new value that needs to be
// assigned into the variable. This value might be nil.
// If CrashIfDeallocating is true, the process is halted if newObj is
// deallocating or newObj’s class does not support weak references.
// If CrashIfDeallocating is false, nil is stored instead.
如果weak指针有指向的对象,那么清除这个对象,然后weak指针指向新的对象;如果weak指针指向新的对象,那么就将新的weak引用存起来;如果weak指针指向的对象被释放了,那么就不会存储这个weak引用,直接返回nil。下面一行行看代码:
template <bool HaveOld, bool HaveNew, bool 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. if (HaveNew && newObj) { 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;}
SideTable是存储了对象的引用信息,结构如下:
struct SideTable { spinlock_t slock; RefcountMap refcnts; weak_table_t weak_table;}
其中weak_table定义如下:
struct weak_table_t { weak_entry_t *weak_entries; size_t num_entries; uintptr_t mask; uintptr_t max_hash_displacement;};
weak_entries是一个散列表,以对象的地址值为key存储weak_entry_t对象。
接下来首先判断weak指针有没有指向旧的对象:
if (HaveOld) { oldObj = *location; oldTable = &SideTables()[oldObj]; } else { oldTable = nil; }
如果有,就取出来,然后将该对象从旧的weak表中删除:
// Clean up old value, if any. if (HaveOld) { weak_unregister_no_lock(&oldTable->weak_table, oldObj, location); }
然后再将weak指针指向新的对象,存入weak表中。
如果weak指针指向的是一个新的对象,那么直接存入对象的weak表中:
// 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. }
其中,weak_register_no_lock函数以被指向的对象的地址为key,将weak指针存入weak表。再看weak_register_no_lock函数的实现:
**整理后关键逻辑如下:** // now remember it and where it is being stored weak_entry_t *entry; if ((entry = weak_entry_for_referent(weak_table, referent))) { append_referrer(entry, referrer); } else { weak_entry_t new_entry; new_entry.referent = referent; new_entry.out_of_line = 0; new_entry.inline_referrers[0] = referrer; for (size_t i = 1; i < WEAK_INLINE_COUNT; i++) { new_entry.inline_referrers[i] = nil; } weak_grow_maybe(weak_table); weak_entry_insert(weak_table, &new_entry); }
其中weak_entry_t定义如下:
#define WEAK_INLINE_COUNT 4struct weak_entry_t { DisguisedPtr<objc_object> referent; union { struct { weak_referrer_t *referrers; uintptr_t out_of_line : 1; uintptr_t num_refs : PTR_MINUS_1; uintptr_t mask; uintptr_t max_hash_displacement; }; struct { // out_of_line=0 is LSB of one of these (don't care which) weak_referrer_t inline_referrers[WEAK_INLINE_COUNT]; }; };};
weak_entry_t对象里边存储着被弱引用的对象,里面weak_referrer_t变量也是一个hash表,存储着该对象的若引用指针。如果弱引用数目小于4个,那么weak_entry_t就通过数组来存储weak指针,如果超过4个,那么就通过hash表来存储weak指针。
回到weak_register_no_lock函数,如果发现被引用的对象有弱引用,那么直接取出weak_entry_t对象,然后将新的weak指针存到weak_referrer_t指向的数组或者hash表中,如果存的是hash表,那么key值是weak指针的地址;如果发现对象之前没有弱引用,那么就新建立一个weak_entry_t对象,然后将weak指针存入到其中。
weak指针的销毁
再来看weak是在什么时候销毁并被置为空的。找到dealloc函数的实现,其中dealloc主要调用了objc_destructInstance函数:
void *objc_destructInstance(id obj) { if (obj) { Class isa = obj->getIsa(); if (isa->hasCxxDtor()) { object_cxxDestruct(obj); } if (isa->instancesHaveAssociatedObjects()) { _object_remove_assocations(obj); } if (!UseGC) objc_clear_deallocating(obj); } return obj;}
objc_destructInstance函数主要做了三件事:
1. 寻找一个名为 ._cxx_destruct 的函数,这一步主要是由编译器插入代码实现,这里销毁了对象的实例变量,并且调用了父类的dealloc(ARC环境下)。
2. 调用_object_remove_assocations函数清除对象的所有关联对象。
3. 调用objc_clear_deallocating函数清除所有的weak指针被将其置为nil。
objc_clear_deallocating函数里主要调用了sidetable_clearDeallocating函数来清除弱引用,里边通过weak_clear_no_lock函数来实现:
void weak_clear_no_lock(weak_table_t *weak_table, id referent_id) { objc_object *referent = (objc_object *)referent_id; weak_entry_t *entry = weak_entry_for_referent(weak_table, referent); if (entry == nil) { /// XXX shouldn't happen, but does with mismatched CF/objc //printf("XXX no entry for clear deallocating %p\n", referent); return; } // zero out references weak_referrer_t *referrers; size_t count; if (entry->out_of_line) { referrers = entry->referrers; count = TABLE_SIZE(entry); } else { referrers = entry->inline_referrers; count = WEAK_INLINE_COUNT; } for (size_t i = 0; i < count; ++i) { objc_object **referrer = referrers[i]; if (referrer) { if (*referrer == referent) { *referrer = nil; } else if (*referrer) { _objc_inform("__weak variable at %p holds %p instead of %p. " "This is probably incorrect use of " "objc_storeWeak() and objc_loadWeak(). " "Break on objc_weak_error to debug.\n", referrer, (void*)*referrer, (void*)referent); objc_weak_error(); } } } weak_entry_remove(weak_table, entry);}
我们可以清楚的看到,这里清除了对象所有的weak指针并将其置为nil,同时从weak表中清除了对应的weak_entry_t对象。
weak和aoturelease
即使将weak指针变量注册到aotureleasepool中,该对象一样有效,这是因为在weak指针变量注册到aotureleasepool中时,系统调用了objc_loadWeakRetained函数,将weak指针变量的引用计数加1,然后又对其发送了objc_autorelease消息,以保证aotureleasepool声明周期中其值可用。
- runtime源码探究(一) weak的实现
- runtime源码探究(二)strong的实现
- runtime源码探究(五)category的加载
- runtime源码探究(三)属性关联
- ObjC Runtime 中 Weak 属性的实现 (上)
- ObjC Runtime 中 Weak 属性的实现 (中)
- iOS开发-Runtime是如何实现weak属性的?
- runtime如何实现weak变量的自动置nil?
- runtime源码探究(四)区分isKindOfClass、isSubclassOfClass、isMemberOfClass
- iOS运行时(runtime)探究一:重要概念
- SGISTL源码探究-pair的实现
- iOS中Block实现的探究(一)
- oc runtime之weak
- Java常用类源码探究(一):Object类
- iOS中的runtime源码简要分析(一)
- weak 的内部实现原理
- @weak - @strong 宏的实现
- weak bind的std实现
- springmvc请求时间参数报错
- 常见排序算法(一)(冒泡排序、插入排序)
- <Android 基础(二十三)> Android Studio快捷键
- 【PowerDesigner】Mysql设计工具 16.5破解
- 如何从github中向eclipse导入一个开源项目(自定义控件)
- runtime源码探究(一) weak的实现
- 1. 什么是Node.js
- WebChromeClient的简单使用
- Git的介绍和安装
- 如何删除GitHub上的项目仓库
- Item4 Know how to view deduced types
- jquery即点即改
- Oracle rownum与分页
- LeetCode 153 Find Minimum in Rotated Sorted Array (二分)