写在前面: iOS底层原理探究是本人在平时的开发和学习中不断积累的一段进阶之
路的。 记录我的不断探索之旅,希望能有帮助到各位读者朋友。
目录如下:
- iOS 底层原理探索 之 alloc
- iOS 底层原理探索 之 结构体内存对齐
- iOS 底层原理探索 之 对象的本质 & isa的底层实现
- iOS 底层原理探索 之 isa - 类的底层原理结构(上)
- iOS 底层原理探索 之 isa - 类的底层原理结构(中)
- iOS 底层原理探索 之 isa - 类的底层原理结构(下)
- iOS 底层原理探索 之 Runtime运行时&方法的本质
- iOS 底层原理探索 之 objc_msgSend
- iOS 底层原理探索 之 Runtime运行时慢速查找流程
- iOS 底层原理探索 之 动态方法决议
- iOS 底层原理探索 之 消息转发流程
- iOS 底层原理探索 之 应用程序加载原理dyld (上)
- iOS 底层原理探索 之 应用程序加载原理dyld (下)
- iOS 底层原理探索 之 类的加载
- iOS 底层原理探索 之 分类的加载
- iOS 底层原理探索 之 关联对象
- iOS底层原理探索 之 魔法师KVC
- iOS底层原理探索 之 KVO原理|8月更文挑战
- iOS底层原理探索 之 重写KVO|8月更文挑战
- iOS底层原理探索 之 多线程原理|8月更文挑战
- iOS底层原理探索 之 GCD函数和队列
- iOS底层原理探索 之 GCD原理(上)
- iOS底层 - 关于死锁,你了解多少?
- iOS底层 - 单例 销毁 可否 ?
- iOS底层 - Dispatch Source
- iOS底层 - 一个栅栏函 拦住了 数
- iOS底层 - 不见不散 的 信号量
- iOS底层 GCD - 一进一出 便成 调度组
- iOS底层原理探索 - 锁的基本使用
- iOS底层 - @synchronized 流程分析
- iOS底层 - 锁的原理探索
- iOS底层 - 带你实现一个读写锁
- iOS底层 - 谈Objective-C block的实现(上)
- iOS底层 - 谈Objective-C block的实现(下)
- iOS底层 - Block, 全面解析!
- iOS底层 - 启动优化(上)
- iOS底层 - 启动优化(下)
- iOS底层原理探索 -- 内存管理 之 内存五大区
- iOS底层原理探索 -- 内存管理 之 Tagged Pointer Format Changes
- iOS底层原理探索 -- 内存管理 之 retain & release
以上内容的总结专栏
细枝末节整理
前言
接着上一篇,我们继续来到OC内存管理系列,关于 弱引用表 的流程。
从 sidetable_retain 开始 今天的源码阅读
id
objc_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
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
/**
* 初始化指向某个对象位置的新弱指针。
* 它将用于以下代码 :
*
* (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
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;
}
haveOld = nil; oldTable = nil;
haveNew = YES; newTable = newTable = &SideTables()[newObj];
haveNew = YES;
newObj = (objc_object *)weak_register_no_lock(&newTable->weak_table, (id)newObj, location, crashIfDeallocating ? CrashIfDeallocating : ReturnNilIfDeallocating);
最后 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,怎么能找到呢?)。