Retain&Release解读

群组
Flutter,iOS,Android,Python等,闲聊工作&技术&问题;
个人主页:https://waitwalker.cn
telegram群链接:https://t.me/joinchat/Ej0o0A1ntlq5ZFIMzzO5Pw

        iOS通过引用计数(Reference Counting)机制来管理内存,当一个对象被创建出来时,它的引用计数从0增加到1,当有外部对象对它进行强引用时,也就是当有额外的指针指向它时,它的引用计数会+1,当该对象收到一条release消息时,它的引用计数会-1;当对象的引用计数为0时,对象将被释放,对象指向的内存被回收.

Retain

        引用计数的增加和减少的原理是什么样的呢?下面我们从源码中来分析一下.以Person *p1 = [[Person alloc]init]为例;

在之前文章中我们总结了一个对象的初始化过程可以总结为分配内存空间并且初始化isa_t结构的过程),我们再把源码贴出来一下(objc_object.h文件中):


inline void

objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)

{

assert(!isTaggedPointer());

// 不是tagged pointer

if (!nonpointer) {

isa.cls = cls;

} else {

assert(!DisableNonpointerIsa);

assert(!cls->instancesRequireRawIsa());

isa_t newisa(0);

#if SUPPORT_INDEXED_ISA

assert(cls->classArrayIndex() > 0);

newisa.bits = ISA_INDEX_MAGIC_VALUE;

// isa.magic is part of ISA_MAGIC_VALUE

// isa.nonpointer is part of ISA_MAGIC_VALUE

newisa.has_cxx_dtor = hasCxxDtor;

newisa.indexcls = (uintptr_t)cls->classArrayIndex();

#else

newisa.bits = ISA_MAGIC_VALUE;

// isa.magic is part of ISA_MAGIC_VALUE

// isa.nonpointer is part of ISA_MAGIC_VALUE

newisa.has_cxx_dtor = hasCxxDtor;

newisa.shiftcls = (uintptr_t)cls >> 3;

#endif

// This write must be performed in a single store in some cases

// (for example when realizing a class because other threads

// may simultaneously try to use the class).

// fixme use atomics here to guarantee single-store and to

// guarantee memory order w.r.t. the class index table

// ...but not too atomic because we don't want to hurt instantiation

isa = newisa;

}

}

        可以看到初始化isa的过程中,并没对保存引用计数的成员变量extra_rc进行操作,我们可以得出一个初步结论,对象在第一次创建的时候,其extra_rc=0;我们知道对象的所有权修饰符默认是strong.其源对象的引用计数的增加也在于有strong指针指向时,即我们示例中的p1强指针,指向了[[Person alloc]init],使[[Person alloc]init]的引用计数从0增加到了1,*p1对象的引用计数此时也就是1了.

        之前我们查看ivar相关接口的时候已经对objc_storeStrong有了初步了解,我们来看看objc_storeStrong的调用栈:


// strong

void

objc_storeStrong(id *location, id obj)

{

id prev = *location;

if (obj == prev) {

return;

}

objc_retain(obj);

*location = obj;

objc_release(prev);

}

看到objc_storeStrong()入口函数内是先调用了objc_retain()新值,然后objc_release()了旧值.

retain调用:


// retain

__attribute__((aligned(16)))

id

objc_retain(id obj)

{

if (!obj) return obj;

if (obj->isTaggedPointer()) return obj;

return obj->retain();

}

inline id

objc_object::retain()

{

assert(!isTaggedPointer());

if (fastpath(!ISA()->hasCustomRR())) {

return rootRetain();

}

return ((id(*)(objc_object *, SEL))objc_msgSend)(this, SEL_retain);

}

ALWAYS_INLINE id

objc_object::rootRetain()

{

return rootRetain(false, false);

}

最终调用的是ALWAYS_INLINE id objc_object::rootRetain(bool tryRetain, bool handleOverflow){}函数


// MARK: - rootRetian

ALWAYS_INLINE id

objc_object::rootRetain(bool tryRetain, bool handleOverflow)

{

if (isTaggedPointer()) return (id)this;

bool sideTableLocked = false;

bool transcribeToSideTable = false;

isa_t oldisa;

isa_t newisa;

do {

transcribeToSideTable = false;

// 首先通过LoadExclusive()加载旧的oldisa

oldisa = LoadExclusive(&isa.bits);

newisa = oldisa;

if (slowpath(!newisa.nonpointer)) {

ClearExclusive(&isa.bits);

if (!tryRetain && sideTableLocked) sidetable_unlock();

if (tryRetain) return sidetable_tryRetain() ? (id)this : nil;

else return sidetable_retain();

}

// don't check newisa.fast_rr; we already called any RR overrides

if (slowpath(tryRetain && newisa.deallocating)) {

ClearExclusive(&isa.bits);

if (!tryRetain && sideTableLocked) sidetable_unlock();

return nil;

}

uintptr_t carry;

// 引用计数加1

newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry); // extra_rc++

// 判断extra_rc位数能否保存引用计数

if (slowpath(carry)) {

// extra_rc溢出;handleOverflow=false

// newisa.extra_rc++ overflowed

if (!handleOverflow) {

ClearExclusive(&isa.bits);

return rootRetain_overflow(tryRetain);

}

// 有溢出 将extra_rc置为最大值的一半

// Leave half of the retain counts inline and

// prepare to copy the other half to the side table.

if (!tryRetain && !sideTableLocked) sidetable_lock();

sideTableLocked = true;

transcribeToSideTable = true;

newisa.extra_rc = RC_HALF;

newisa.has_sidetable_rc = true;

}

} while (slowpath(!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)));//存储

// 有溢出 将另一半的引用计数拷贝到side table里面

if (slowpath(transcribeToSideTable)) {

// Copy the other half of the retain counts to the side table.

sidetable_addExtraRC_nolock(RC_HALF);

}

if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock();

// 返回自身

return (id)this;

}

其逻辑过程:

1.1)首先调用LoadExclusive()函数获取旧isa,然后赋值给新的isa实例:newisa;

1.2)调用addc()函数,将extra_rc++;

1.3)调用StoreExclusive()更新isa的引用计数,最后返回更新后的isa;这个过程没有处理溢出情况

2.1)如果if (slowpath(carry)) {}为真,判断handleOverflow是否为false,为false,调用rootRetain_overflow()处理溢出逻辑


NEVER_INLINE id

objc_object::rootRetain_overflow(bool tryRetain)

{

return rootRetain(tryRetain, true);

}

rootRetain_overflow()内部调用的还是ALWAYS_INLINE id objc_object::rootRetain(bool tryRetain, bool handleOverflow)()函数,只是第二个参数handleOverflow传的是true.所以走到:


// 有溢出 将extra_rc置为最大值的一半

// Leave half of the retain counts inline and

// prepare to copy the other half to the side table.

if (!tryRetain && !sideTableLocked) sidetable_lock();

sideTableLocked = true;

transcribeToSideTable = true;

newisa.extra_rc = RC_HALF;

newisa.has_sidetable_rc = true;

2.2)将extra_rc值置为最大值的一半,更新has_sidetable_rc标记为true,即sideTable中存储了引用计数.

2.3)调用StoreExclusive()更新isa的引用计数

2.4)将将另一半的引用计数拷贝到side table里面


// 有溢出 将另一半的引用计数拷贝到side table里面

if (slowpath(transcribeToSideTable)) {

// Copy the other half of the retain counts to the side table.

sidetable_addExtraRC_nolock(RC_HALF);

}

// MARK: - 将一些引用计数保存在side table中

// Move some retain counts to the side table from the isa field.

// Returns true if the object is now pinned.

bool

objc_object::sidetable_addExtraRC_nolock(size_t delta_rc)

{

assert(isa.nonpointer);

SideTable& table = SideTables()[this];

size_t& refcntStorage = table.refcnts[this];

size_t oldRefcnt = refcntStorage;

// isa-side bits should not be set here

assert((oldRefcnt & SIDE_TABLE_DEALLOCATING) == 0);

assert((oldRefcnt & SIDE_TABLE_WEAKLY_REFERENCED) == 0);

if (oldRefcnt & SIDE_TABLE_RC_PINNED) return true;

uintptr_t carry;

size_t newRefcnt =

addc(oldRefcnt, delta_rc << SIDE_TABLE_RC_SHIFT, 0, &carry);

if (carry) {

refcntStorage =

SIDE_TABLE_RC_PINNED | (oldRefcnt & SIDE_TABLE_FLAG_MASK);

return true;

}

else {

refcntStorage = newRefcnt;

return false;

}

}

之前我们提到过sideTable:


// MARK: - sideTabel

struct SideTable {

spinlock_t slock;

RefcountMap refcnts;将另一半的引用计数拷贝到side table里面

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有三个成员变量:spinlock_t(自旋锁),RefcountMap(保存引用计数hash表),weak_table_t(保存weak指针hash表);

2.5)返回对象自身

以上就是retain的源码实现逻辑,思路还是很清晰.其中alloc/new/copy/mutableCopy会增加引用计数.

Release

release可以理解为retain的”逆向操作”,其入口函数:


// MARK: - release

__attribute__((aligned(16)))

void

objc_release(id obj)

{

if (!obj) return;

if (obj->isTaggedPointer()) return;

return obj->release();

}

// Equivalent to calling [this release], with shortcuts if there is no override

inline void

objc_object::release()

{

assert(!isTaggedPointer());

if (fastpath(!ISA()->hasCustomRR())) {

rootRelease();

return;

}

((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_release);

}

可以看到release函数最终调用的是ALWAYS_INLINE bool objc_object::rootRelease(bool performDealloc, bool handleUnderflow){};并且默认是没有引用计数溢出的情况.


ALWAYS_INLINE bool

objc_object::rootRelease(bool performDealloc, bool handleUnderflow)

{

if (isTaggedPointer()) return false;

bool sideTableLocked = false;

isa_t oldisa;

isa_t newisa;

retry:

do {

// 首先加载旧的isa

oldisa = LoadExclusive(&isa.bits);

// 将旧的isa赋值给newisa

newisa = oldisa;

if (slowpath(!newisa.nonpointer)) {

ClearExclusive(&isa.bits);

if (sideTableLocked) sidetable_unlock();

return sidetable_release(performDealloc);

}

// don't check newisa.fast_rr; we already called any RR overrides

uintptr_t carry;

// 将extra_rc减1

newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry); // extra_rc--

// 如果有越界的情况 即extra_rc < 0,走underflow

if (slowpath(carry)) {

// don't ClearExclusive()

goto underflow;

}

} while (slowpath(!StoreReleaseExclusive(&isa.bits,

oldisa.bits, newisa.bits)));//更新isa的extra_rc

if (slowpath(sideTableLocked)) sidetable_unlock();

return false;

underflow:

// newisa.extra_rc-- underflowed: borrow from side table or deallocate

// abandon newisa to undo the decrement

newisa = oldisa;

// 先判断isa的has_sidetable_rc是否为true

if (slowpath(newisa.has_sidetable_rc)) {

if (!handleUnderflow) {

ClearExclusive(&isa.bits);

// 调用rootRelease_underflow,处理越界情况

return rootRelease_underflow(performDealloc);

}

// Transfer retain count from side table to inline storage.

if (!sideTableLocked) {

ClearExclusive(&isa.bits);

sidetable_lock();

sideTableLocked = true;

// Need to start over to avoid a race against

// the nonpointer -> raw pointer transition.

goto retry;

}

// 从side table中获取引用计数

// Try to remove some retain counts from the side table.

size_t borrowed = sidetable_subExtraRC_nolock(RC_HALF);

// To avoid races, has_sidetable_rc must remain set

// even if the side table count is now zero.

if (borrowed > 0) {

// 如果borrowed(side table)的值大于1,将extra_rc中的引用计数赋值给extra_rc

// Side table retain count decreased.

// Try to add them to the inline count.

newisa.extra_rc = borrowed - 1; // redo the original decrement too

bool stored = StoreReleaseExclusive(&isa.bits,

oldisa.bits, newisa.bits);

if (!stored) {

// Inline update failed.

// Try it again right now. This prevents livelock on LL/SC

// architectures where the side table access itself may have

// dropped the reservation.

isa_t oldisa2 = LoadExclusive(&isa.bits);

isa_t newisa2 = oldisa2;

if (newisa2.nonpointer) {

uintptr_t overflow;

newisa2.bits =

addc(newisa2.bits, RC_ONE * (borrowed-1), 0, &overflow);

if (!overflow) {

stored = StoreReleaseExclusive(&isa.bits, oldisa2.bits,

newisa2.bits);

}

}

}

if (!stored) {

// Inline update failed.

// Put the retains back in the side table.

sidetable_addExtraRC_nolock(borrowed);

goto retry;

}

// Decrement successful after borrowing from side table.

// This decrement cannot be the deallocating decrement - the side

// table lock and has_sidetable_rc bit ensure that if everyone

// else tried to -release while we worked, the last one would block.

sidetable_unlock();

return false;

}

else {

// 如果side table中值为空,则执行dealloc

// Side table is empty after all. Fall-through to the dealloc path.

}

}

// Really deallocate.

if (slowpath(newisa.deallocating)) {

ClearExclusive(&isa.bits);

if (sideTableLocked) sidetable_unlock();

return overrelease_error();

// does not actually return

}

newisa.deallocating = true;

if (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)) goto retry;

if (slowpath(sideTableLocked)) sidetable_unlock();

__sync_synchronize();

if (performDealloc) {

((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);

}

return true;

}

逻辑过程:

1.1)调用LoadExclusive()函数,获取旧的isa,赋值给新的newisa

1.2)调用subc()函数,将extra_rc减1,如果carry有越界情况,走到2.1)

1.3)调用StoreReleaseExclusive()函数,更新返回isa

2.1)carry有越界,说明side table保存了引用计数,并且has_sidetable_rc=true

2.2)调用rootRelease_underflow()函数,传入参数handleUnderflow=true

2.3)调用sidetable_subExtraRC_nolock()获取引用计数,side table的引用计数大于0,将side table中的引用计数减1后赋值给extra_rc;否则走3.1)

2.4)调用StoreReleaseExclusive()函数,更新返回isa

3.1)side table中的引用计数为0,则调用dealloc方法


// Really deallocate.

if (slowpath(newisa.deallocating)) {

ClearExclusive(&isa.bits);

if (sideTableLocked) sidetable_unlock();

return overrelease_error();

// does not actually return

}

newisa.deallocating = true;

if (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)) goto retry;

if (slowpath(sideTableLocked)) sidetable_unlock();

__sync_synchronize();

if (performDealloc) {

((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);

}

        release总结下来就是,先将extra_rc减1,如果extra_rc=0,查看side table中是否有引用计数,有则将side table中值减1后赋值给extra_rc;side table中没有值,说明引用计数为0,调用dealloc函数,整体逻辑思路也是很清晰的.

RetainCount

retainCount是获取对象的引用计数.


// MARK: - 获取对象的引用计数

inline uintptr_t

objc_object::rootRetainCount()

{

if (isTaggedPointer()) return (uintptr_t)this;

sidetable_lock();

isa_t bits = LoadExclusive(&isa.bits);

ClearExclusive(&isa.bits);

if (bits.nonpointer) {

// 引用计数 = 1 + extra_rc

uintptr_t rc = 1 + bits.extra_rc;

// 如果side table中有值

if (bits.has_sidetable_rc) {

// 再加上side table中的引用计数

rc += sidetable_getExtraRC_nolock();

}

sidetable_unlock();

return rc;

}

sidetable_unlock();

return sidetable_retainCount();

}

        先LoadExclusive()获取isa,将extra_rc加1赋值给rc,然后判断has_sidetable_rc,side table中是否有引用计数,有的话,再加上side table中的引用计数,然后返回.

编译后源码库

编译后的源码放在Github, 如果对你有帮助,请给一个star吧!

博客地址&相关文章

博客地址: https://waitwalker.github.io/

系列文章:

1. Runtime源码编译

2. objc_object解读

3. Method解读

4. Class解读

5. Ivar objc_property_t Protocol解读

6. Block解读

7. Retain&Release解读

8. Autorelease解读

9. Weak解读

10. msgSend()解读


  转载请注明: waitwalker Retain&Release解读

  目录