weak原理
弱引用指针添加到弱引用表。
NSObject.mm
id
objc_initWeak(id *location, id newObj)//location是当前对象的地址 newObj是要weak的对象
{
if (!newObj) {
*location = nil;
return nil;
}
//C++模版函数 第一次进来DontHaveOld是false DoHaveNew是true
return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
(location, (objc_object*)newObj);
}
static id
storeWeak(id *location, objc_object *newObj)
{
ASSERT(haveOld || haveNew);
if (!haveNew) ASSERT(newObj == nil);
Class previouslyInitializedClass = nil;
id oldObj;
//SideTable用来管理引用计数和弱引用表
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(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;
}
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);
};
弱引用的指针存储到弱引用表
通过哈希运算找到弱引用表的地址,然后把弱引用指针插入到弱引用表。
总结:
当前runtime维护了一张全局的弱引用表,也是哈希表,全局的弱引用表保存了所有的弱引用对象。
weak表(弱引用表):
key:当前对象地址(因为一个对象在内存中的地址是不变的)
value:weak指针的地址,是一个数组 存储所有和相关对象的弱引用指针。weak指针的地址指向当前对象的地址。
调用流程:
-
objc_initWeak
objc_initWeak调用storeWeak存储weak
-
store_weak
storeWeak方法中SideTable用来管理引用计数和弱引用表,根据当前对象的指针通过哈希运算把当前对象的SideTable取出来。
store_weak中调用weak_register,weak_unregister
-
weak_register_no_lock
弱引用指针存储到弱引用表。通过哈希运算,放入weak_table
weak_register_no_lock参数是:当前对象的弱引用表,当前对象,地址指针。
weak_register_no_lock方法里面调用weak_entry_for_referent,把当前要弱引用的对象添加到弱引用表。
-
weak_entry_for_referent
weak_entry_for_referent方法里面通过哈希运算找到当前弱引用表的地址,然后插入。
-
-
setWeaklyReferenced_nolock
store_weak中执行完weak_register_no_lock之后,又调用了setWeaklyReferenced_nolock,把当前对象的weakly_referenced置为true,表明当前对象是一个弱引用对象。
-
-
objc_destroyWeak
声明weak要不断的通过hash计算来找到地址然后取出表,来进行查找。
声明太多的weak比较耗费性能。只在解决循环引用的时候使用。
网友评论