前言
接着上一篇,我们继续来到OC内存管理系列,关于弱引用表的流程。
从 sidetable_retain 开始 今天的源码阅读
代码语言:javascript复制idobjc_object::sidetable_retain(bool locked){#if SUPPORT_NONPOINTER_ISA ASSERT(!isa.nonpointer);#endif SideTable& table = SideTables()[this]; if (!locked) table.lock(); size_t& refcntStorage = table.refcnts[this]; if (! (refcntStorage & SIDE_TABLE_RC_PINNED)) { refcntStorage = SIDE_TABLE_RC_ONE; } table.unlock(); return (id)this;}
(滑动显示更多)
SideTable
代码语言:javascript复制struct SideTable {
spinlock_t slock;
// 引用计数表
RefcountMap refcnts;
// 弱引用表 ( __weak )
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);
};
(滑动显示更多)
系统会维护多张 SideTable ,( 如果只有一张表的话,所有的对象,使用中会十分的耗费性能(查询,锁操作) ),多张表就会将对象分开存储,随着使用,可以对释放的对象进行表的存储的优化。空间换时间的常规操作。
弱引用表
从测试打印 看起来不太科学开始:
汇编搞一下
其会来到 objc_initWeak ,这是为什么呢?因为在 llvm 符号绑定了。和之前的 super 一样,会找到特定的标识符。
objc_initWeak
代码语言:javascript复制/**
* 初始化指向某个对象位置的新弱指针。
* 它将用于以下代码 :
*
* (The nil case)
* __weak id weakPtr;
* (The non-nil case)
* NSObject *o = ...;
* __weak id weakPtr = o;
*
* 这个函数对于并发来说不是线程安全的
* 对弱变量的修改。(并发弱清除是安全的。)
*
* @param location Address of __weak ptr.
* @param newObj Object ptr.
*/
id
objc_initWeak(id *location, id newObj)
{
if (!newObj) {
*location = nil;
return nil;
}
return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
(location, (objc_object*)newObj);
}
(滑动显示更多)
storeWeak
代码语言:javascript复制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;
// 为旧值和新值获取锁。
// 按锁地址订购,防止锁顺序问题。
// 如果我们下面的旧值发生了变化,请重试。
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;
}
// 防止弱引用机制之间的死锁
// 和类初始化机制,确保没有
// 弱引用对象具有未初始化的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;
}
}
// 清除旧的价值,如果有的话。
if (haveOld) {
weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
}
// 如果有新的值,则分配新的值
if (haveNew) {
newObj = (objc_object *)
weak_register_no_lock(&newTable->weak_table, (id)newObj, location,
crashIfDeallocating ? CrashIfDeallocating : ReturnNilIfDeallocating);
// weak_register_no_lock returns nil if weak store should be rejected
// Set is-weakly-referenced bit in refcount table.
if (!newObj->isTaggedPointerOrNil()) {
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);
// This must be called without the locks held, as it can invoke
// arbitrary code. In particular, even if _setWeaklyReferenced
// is not implemented, resolveInstanceMethod: may be, and may
// call back into the weak reference machinery.
callSetWeaklyReferenced((id)newObj);
return (id)newObj;
}
(滑动显示更多)
代码语言:javascript复制haveOld = nil; oldTable = nil;
haveNew = YES; newTable = newTable = &SideTables()[newObj];
(滑动显示更多)
最后 return 出去;
weak流程总结
- 1:首先我们知道有一个非常牛逼的家伙-sideTable
- 2:得到sideTable的weakTable 弱引用表
- 3:创建一个weak_entry_t
- 4:把referent加入到weak_entry_t的数组inline_referrers
- 5:把weak_table扩容一下
- 6:把new_entry加入到weak_table中
回到开头 __weak 那里为什么会打印 2 呢?
我们断点打上, 汇编看一下:
可以看到是调用了 objc_loadWeakRetained 加下来来到
objc_loadWeakRetained
在这里做了一次 obj->rootTryRetain() 的操作,也就是会走我们之前探索的 retain 流程;
苹果为什么会这么操作呢?
再看:
也就是说 weakObjc 和 objc 两者是相互独立的,在 中间的代码块之后, weakObjc 指向了nil;和objc之间是没有关系的;weakObjc 只是加载弱引用表中。
最后会崩,因为找不到了弱引用表(objc 为 nil,怎么能找到呢?)。