retain实现原理
retain的源码:
- //使用此方法等价于使用[this retain]
- inline id
- objc_object::retain()
- {
- //确保对象不是tagged pointer
- ASSERT(!isTaggedPointer());
- return rootRetain(false, RRVariant::FastOrMsgSend);
- }
复制代码- ALWAYS_INLINE id
- objc_object::rootRetain()
- {
- //分为快速路径和慢速路径,以优化性能和处理溢出情况。
- return rootRetain(false, RRVariant::Fast);
- }
复制代码 这段代码的逻辑是:起首初始化并赋值变量,然后进入循环,查抄 newisa 是否为 nonpointer isa,如果不是,则清除 isa 的原子锁。如果 tryRetain 为真,则实行调用 sidetable_tryRetain 增加引用计数,否则调用 sidetable_retain 增加引用计数。如果对象正在被释放,则清除 isa 的原子锁,并根据条件返回 nil 或对象。接着,实行增加引用计数并查抄是否溢出,如果溢出,则将部分引用计数转移到 side table 并设置相干标记。循环结束时,查抄并处理 side table 的引用计数操作。
我们再看看sidetable_tryRetain方法:
- bool
- objc_object::sidetable_tryRetain()
- {
- #if SUPPORT_NONPOINTER_ISA
- //确保对象的 isa 不是 nonpointer isa(如果支持 nonpointer isa)
- ASSERT(!isa().nonpointer);
- #endif
- //从全局的sideTable中获取当前对象的sideTable
- SideTable& table = SideTables()[this];
- // NO SPINLOCK HERE
- // _objc_rootTryRetain() is called exclusively by _objc_loadWeak(),
- // which already acquired the lock on our behalf.
- // fixme can't do this efficiently with os_lock_handoff_s
- // if (table.slock == 0) {
- // _objc_fatal("Do not call -_tryRetain.");
- // }
- bool result = true;
- //在 SideTable 的 refcnts 中尝试插入当前对象。如果插入成功,则表示该对象以前没有引用计数记录,新建的条目引用计数为 1。
- auto it = table.refcnts.try_emplace(this, SIDE_TABLE_RC_ONE);
- //获取并引用映射中元素的值部分
- auto &refcnt = it.first->second;
- //如果插入了新条目(即条目不存在),什么也不做,因为条目已经初始化为 SIDE_TABLE_RC_ONE。
- if (it.second) {
- //如果条目已经存在,检查是否有 SIDE_TABLE_DEALLOCATING 标志。如果有,设置 result 为 false,表示无法增加引用计数。
- } else if (refcnt & SIDE_TABLE_DEALLOCATING) {
- result = false;
- //如果条目已经存在并且没有 SIDE_TABLE_DEALLOCATING 标志,再检查是否有 SIDE_TABLE_RC_PINNED 标志。如果没有,增加引用计数 SIDE_TABLE_RC_ONE。
- } else if (! (refcnt & SIDE_TABLE_RC_PINNED)) {
- refcnt += SIDE_TABLE_RC_ONE;
- }
-
- return result;
- }
复制代码 这段代码用于实行增加对象的引用计数。
起首确保对象的 isa 不是 nonpointer isa,然后从全局的 SideTables 中获取当前对象的 SideTable。
在 SideTable 的 refcnts 中实行插入当前对象。如果插入成功,则表示该对象从前没有引用计数记录,新建的条目引用计数为 1。如果对象已经存在于 SideTable 中,查抄引用计数标记:
如果对象正在被释放(DEALLOCATING),返回 false,表示增加引用计数失败。
如果引用计数没有被固定(没有 SIDE_TABLE_RC_PINNED 标记),增加引用计数。
retain总体流程大概如下图:
release
源码:
- // Equivalent to calling [this release], with shortcuts if there is no override
- inline void
- objc_object::release()
- {
- ASSERT(!isTaggedPointer());
- rootRelease(true, RRVariant::FastOrMsgSend);
- }
- ALWAYS_INLINE bool
- objc_object::rootRelease()
- {
- return rootRelease(true, RRVariant::Fast);
- }
- inline void
- objc_object::release()
- {
- ASSERT(!isTaggedPointer());
- if (fastpath(!ISA()->hasCustomRR())) {
- // Standard RR of a class is a no-op.
- if (!ISA()->isMetaClass())
- sidetable_release();
- return;
- }
- ((void(*)(objc_object *, SEL))objc_msgSend)(this, @selector(release));
- }
复制代码- ALWAYS_INLINE bool
- objc_object::rootRelease(bool performDealloc, objc_object::RRVariant variant)
- {
- // 如果是标记指针,直接返回 false
- if (slowpath(isTaggedPointer())) return false;
- bool sideTableLocked = false; // 用于标记侧表是否被锁定
- isa_t newisa, oldisa; // 定义 isa_t 类型的变量 newisa 和 oldisa
- // 加载 isa 值到 oldisa 中
- oldisa = LoadExclusive(&isa().bits);
- // 如果引用计数变种是 FastOrMsgSend
- if (variant == RRVariant::FastOrMsgSend) {
- // 这些检查仅对 objc_release() 有意义
- // 它们在这里是为了避免重新加载 isa
- if (slowpath(oldisa.getDecodedClass(false)->hasCustomRR())) {
- ClearExclusive(&isa().bits); // 清除独占标记
- if (oldisa.getDecodedClass(false)->canCallSwiftRR()) {
- // 如果可以调用 Swift 的引用计数方法
- swiftRelease.load(memory_order_relaxed)((id)this);
- return true;
- }
- // 调用 objc_msgSend 的 release 方法
- ((void(*)(objc_object *, SEL))objc_msgSend)(this, @selector(release));
- return true;
- }
- }
- // 检查 isa 是否为指针形式
- if (slowpath(!oldisa.nonpointer)) {
- // 一个类永远是一个类,所以我们可以在 CAS 循环外进行此检查
- if (oldisa.getDecodedClass(false)->isMetaClass()) {
- ClearExclusive(&isa().bits); // 清除独占标记
- return false;
- }
- }
- #if !ISA_HAS_INLINE_RC
- // 如果不支持内联引用计数,使用侧表
- ClearExclusive(&isa().bits); // 清除独占标记
- return sidetable_release(sideTableLocked, performDealloc);
- #else
- retry:
- do {
- newisa = oldisa; // 将 oldisa 赋值给 newisa
- if (slowpath(!newisa.nonpointer)) {
- ClearExclusive(&isa().bits); // 清除独占标记
- return sidetable_release(sideTableLocked, performDealloc);
- }
- if (slowpath(newisa.isDeallocating())) {
- ClearExclusive(&isa().bits); // 清除独占标记
- if (sideTableLocked) {
- ASSERT(variant == RRVariant::Full);
- sidetable_unlock(); // 解锁侧表
- }
- return false;
- }
- // 不检查 newisa.fast_rr; 我们已经调用了任何 RR 重载
- uintptr_t carry;
- newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry); // extra_rc--
- if (slowpath(carry)) {
- // 不清除独占标记
- goto underflow;
- }
- } while (slowpath(!StoreReleaseExclusive(&isa().bits, &oldisa.bits, newisa.bits)));
- if (slowpath(newisa.isDeallocating()))
- goto deallocate;
- if (variant == RRVariant::Full) {
- if (slowpath(sideTableLocked)) sidetable_unlock();
- } else {
- ASSERT(!sideTableLocked);
- }
- return false;
- underflow:
- // newisa.extra_rc-- 下溢:从侧表借用或析构
- // 放弃 newisa 以撤销递减
- newisa = oldisa;
- if (slowpath(newisa.has_sidetable_rc)) {
- if (variant != RRVariant::Full) {
- ClearExclusive(&isa().bits); // 清除独占标记
- return rootRelease_underflow(performDealloc);
- }
- // 将引用计数从侧表转移到内联存储
- if (!sideTableLocked) {
- ClearExclusive(&isa().bits); // 清除独占标记
- sidetable_lock(); // 锁定侧表
- sideTableLocked = true;
- // 需要重新开始以避免与指针非指针转换的竞争
- oldisa = LoadExclusive(&isa().bits);
- goto retry;
- }
- // 尝试从侧表中删除一些引用计数
- auto borrow = sidetable_subExtraRC_nolock(RC_HALF);
- bool emptySideTable = borrow.remaining == 0; // 如果侧表中没有引用计数,将清空侧表
- if (borrow.borrowed > 0) {
- // 侧表引用计数减少
- // 尝试将它们添加到内联计数
- bool didTransitionToDeallocating = false;
- newisa.extra_rc = borrow.borrowed - 1; // 重新执行原始递减
- newisa.has_sidetable_rc = !emptySideTable;
- bool stored = StoreReleaseExclusive(&isa().bits, &oldisa.bits, newisa.bits);
- if (!stored && oldisa.nonpointer) {
- // 内联更新失败
- // 立即重试。这可以防止在 LL/SC 架构上发生活锁,
- // 因为侧表访问本身可能会丢失预留
- uintptr_t overflow;
- newisa.bits =
- addc(oldisa.bits, RC_ONE * (borrow.borrowed-1), 0, &overflow);
- newisa.has_sidetable_rc = !emptySideTable;
- if (!overflow) {
- stored = StoreReleaseExclusive(&isa().bits, &oldisa.bits, newisa.bits);
- if (stored) {
- didTransitionToDeallocating = newisa.isDeallocating();
- }
- }
- }
- if (!stored) {
- // 内联更新失败
- // 将保留重新放回侧表
- ClearExclusive(&isa().bits);
- sidetable_addExtraRC_nolock(borrow.borrowed);
- oldisa = LoadExclusive(&isa().bits);
- goto retry;
- }
- // 从侧表借用后的递减成功
- if (emptySideTable)
- sidetable_clearExtraRC_nolock();
- if (!didTransitionToDeallocating) {
- if (slowpath(sideTableLocked)) sidetable_unlock();
- return false;
- }
- }
- else {
- // 侧表最终为空,进入析构路径
- }
- }
- deallocate:
- // 真的要析构了
- ASSERT(newisa.isDeallocating());
- ASSERT(isa().isDeallocating());
- if (slowpath(sideTableLocked)) sidetable_unlock();
- __c11_atomic_thread_fence(__ATOMIC_ACQUIRE);
- if (performDealloc) {
- this->performDealloc();
- }
- return true;
- #endif // ISA_HAS_INLINE_RC
- }
复制代码- uintptr_t
- objc_object::sidetable_release(bool locked, bool performDealloc)
- {
- #if SUPPORT_NONPOINTER_ISA
- ASSERT(!isa().nonpointer);
- #endif
- // 获取当前对象的侧表
- SideTable& table = SideTables()[this];
- bool do_dealloc = false; // 标记是否需要析构
- // 如果未锁定侧表,先锁定它
- if (!locked) table.lock();
-
- // 尝试在侧表的引用计数字典中插入一个新条目
- // 如果该对象不在侧表中,插入 {this, SIDE_TABLE_DEALLOCATING}
- auto it = table.refcnts.try_emplace(this, SIDE_TABLE_DEALLOCATING);
- auto &refcnt = it.first->second; // 获取引用计数
- //判断是否进行了插入操作
- if (it.second) {
- // 如果对象之前不在侧表中,表示这是第一次插入
- // 设置 do_dealloc 为 true,表示需要析构
- do_dealloc = true;
- } else if (refcnt < SIDE_TABLE_DEALLOCATING) {
- // 如果引用计数小于 SIDE_TABLE_DEALLOCATING
- // 表示引用计数为负数,可能设置了 SIDE_TABLE_WEAKLY_REFERENCED
- // 设置 do_dealloc 为 true,并将引用计数标记为 SIDE_TABLE_DEALLOCATING
- do_dealloc = true;
- refcnt |= SIDE_TABLE_DEALLOCATING;
- } else if (! (refcnt & SIDE_TABLE_RC_PINNED)) {
- // 如果引用计数未固定(未设置 SIDE_TABLE_RC_PINNED)
- // 将引用计数减一
- refcnt -= SIDE_TABLE_RC_ONE;
- }
- // 解锁侧表
- table.unlock();
- // 如果需要析构且 performDealloc 为真,执行析构操作
- if (do_dealloc && performDealloc) {
- this->performDealloc();
- }
- // 返回是否需要析构
- return do_dealloc;
- }
复制代码 实现了 objc_object 的引用计数淘汰操作,并根据引用计数的变革决定是否需要进行对象的析构。
dealloc
dealloc用于在对象的引用计数为0的时候,释放该对象,那么在底层,它是怎样实现的呢.
- inline void objc_object::rootDealloc()
- {
- // 如果对象是 tagged pointer(标记指针),直接返回
- if (isTaggedPointer()) return; // fixme necessary?
-
- #if !ISA_HAS_INLINE_RC
- // 如果没有内联引用计数,直接调用 object_dispose 释放对象
- object_dispose((id)this);
- #else
- // 如果有内联引用计数,并且满足以下所有条件,则直接释放内存
- if (fastpath(isa().nonpointer && // 非指针 ISA
- !isa().weakly_referenced && // 没有弱引用
- !isa().has_assoc && // 没有关联对象
- #if ISA_HAS_CXX_DTOR_BIT
- !isa().has_cxx_dtor && // 没有 C++ 析构函数
- #else
- !isa().getClass(false)->hasCxxDtor() && // 没有 C++ 析构函数
- #endif
- !isa().has_sidetable_rc)) // 没有使用 side table 引用计数
- {
- // 确认没有 side table 存在
- assert(!sidetable_present());
- // 直接释放内存
- free(this);
- }
- else {
- // 否则,调用 object_dispose 释放对象
- object_dispose((id)this);
- }
- #endif // ISA_HAS_INLINE_RC
- }
复制代码 用于销毁和释放对象:
- id object_dispose(id obj)
- {
- // 如果 obj 是空指针,则直接返回 nil
- if (!obj) return nil;
- // 调用 objc_destructInstance 函数,销毁对象实例的内容
- objc_destructInstance(obj);
-
- // 释放对象所占用的内存
- free(obj);
- // 返回 nil,表示对象已被销毁
- return nil;
- }
复制代码- objc_object::clearDeallocating()
- {
- if (slowpath(!isa.nonpointer)) {
- // Slow path for raw pointer isa.
- // 如果要释放的对象没有采用了优化过的isa引用计数
- sidetable_clearDeallocating();
- }
- else if (slowpath(isa.weakly_referenced || isa.has_sidetable_rc)) {
- // Slow path for non-pointer isa with weak refs and/or side table data.
- // 如果要释放的对象采用了优化过的isa引用计数,并且有弱引用或者使用了sideTable的辅助引用计数
- clearDeallocating_slow();
- }
- //确保 side table 中没有该对象的引用计数记录。
- assert(!sidetable_present());
- }
复制代码 上面这段代码根据是否采用了优化过的isa做引用计数分为两种:
- 要释放的对象没有采用优化过的isa引用计数:会调用sidetable_clearDeallocating() 函数在 side table 中整理对象的引用计数。
- void
- objc_object::sidetable_clearDeallocating()
- {
- // 在全局的SideTables中,以this指针(要释放的对象)为key,找到对应的SideTable
- SideTable& table = SideTables()[this];
- // clear any weak table items
- // clear extra retain count and deallocating bit
- // (fixme warn or abort if extra retain count == 0 ?)
- table.lock();
- //在散列表SideTable中找到对应的引用计数表RefcountMap,拿到要释放的对象的引用计数
- RefcountMap::iterator it = table.refcnts.find(this);
- if (it != table.refcnts.end()) {
- //如果要释放的对象被弱引用了,通过weak_clear_no_lock函数将指向该对象的弱引用指针置为nil
- if (it->second & SIDE_TABLE_WEAKLY_REFERENCED) {
- weak_clear_no_lock(&table.weak_table, (id)this);
- }
- //从引用计数表中擦除该对象的引用计数
- table.refcnts.erase(it);
- }
- table.unlock();
- }
复制代码 先找到对应的 SideTable,并对其加锁。
查找引用计数表 refcnts 中该对象的引用计数。
如果找到该对象的引用计数且被弱引用,则整理弱引用。
末了擦除该对象的引用计数,并解锁 SideTable。
- 如果该对象采用了优化过的isa引用计数而且该对象有弱引用或者使用了sideTable的辅助引用计数,就会调用clearDeallocating_slow()函数处理引用计数
- NEVER_INLINE void
- objc_object::clearDeallocating_slow()
- {
- assert(isa.nonpointer && (isa.weakly_referenced || isa.has_sidetable_rc));
- // 在全局的SideTables中,以this指针(要释放的对象)为key,找到对应的SideTable
- SideTable& table = SideTables()[this];
- table.lock();
- if (isa.weakly_referenced) {
- //要释放的对象被弱引用了,通过weak_clear_no_lock函数将指向该对象的弱引用指针置为nil
- weak_clear_no_lock(&table.weak_table, (id)this);
- }
- //使用了sideTable的辅助引用计数,直接在SideTable中擦除该对象的引用计数
- if (isa.has_sidetable_rc) {
- table.refcnts.erase(this);
- }
- table.unlock();
- }
复制代码 找到对应的 SideTable,并对其加锁。
如果对象被弱引用,整理弱引用。
如果对象使用了 side table 的引用计数,擦除该对象的引用计数。
末了解锁 SideTable。
以上两种情况都涉及weak_clear_no_lock函数, 它的作用就是将被弱引用对象的弱引用指针置为nil.
dealloc的流程:如果对象是 tagged pointer,则直接返回。如果没有内联引用计数,调用 object_dispose 释放对象;如果有内联引用计数,而且对象满意非指针 ISA、没有弱引用、没有关联对象、没有 C++ 析构函数以及没有使用 side table 引用计数等条件,则直接释放内存。否则,调用 object_dispose 进行标准的对象销毁和内存释放。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |