Objective-C
内存管理的核心思想就是经过对象的引用计数来对内存对象的生命周期进行控制。说直白一点,就是调用retain
会加1,调用release
就会减1,引用计数清零或者调用dealloc
就销毁。c++
引用计数
引用计数,即为对象被持有的次数。是内存管理的核心点。下面咱们来看一个关于引用计数的例子:安全
- (void)testRefCount {
NSObject *obj = [NSObject alloc];
NSLog(@"==refCount==%ld==", (long)CFGetRetainCount((__bridge CFTypeRef)(obj)));
}
复制代码
运行程序,结果为1。但是alloc
的流程中并无对引用计数操做的流程,那么这个打印为何是1呢?来看看retainCount
的源码:ide
- (NSUInteger)retainCount {
return ((id)self)->rootRetainCount();
}
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) {
uintptr_t rc = 1 + bits.extra_rc;
if (bits.has_sidetable_rc) {
rc += sidetable_getExtraRC_nolock();
}
sidetable_unlock();
return rc;
}
sidetable_unlock();
return sidetable_retainCount();
}
复制代码
能够看到,引用计数的总值就是isa
里面extra_rc
的取值和散列表中引用计数表的取值外加1,咱们新alloc
的对象,引用计数打印为1就是由于这个加的这个1,其实新alloc
出来的对象引用计数为0。函数
那么引用计数是存储在哪里的,retain
和release
究竟是如何处理的呢?下面咱们先来看一下retain
:post
retain
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);
}
复制代码
能够看到,调用objc_retain
首先会判断是不是isTaggedPointer
,若是是就直接返回。接着会判断对象没有自定义retain/release
方法就会调用rootRetain
,不然就经过objc_msgSend
发送SEL_retain
消息。性能
id
objc_object::rootRetain()
{
return rootRetain(false, false);
}
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;
// 获取isa
oldisa = LoadExclusive(&isa.bits);
newisa = oldisa;
if (slowpath(!newisa.nonpointer)) {
// 不是nonpointer isa 散列表处理
ClearExclusive(&isa.bits);
if (!tryRetain && sideTableLocked) sidetable_unlock();
if (tryRetain) return sidetable_tryRetain() ? (id)this : nil;
else return sidetable_retain();
}
// 若是正在deallocating 不作处理
if (slowpath(tryRetain && newisa.deallocating)) {
ClearExclusive(&isa.bits);
if (!tryRetain && sideTableLocked) sidetable_unlock();
return nil;
}
// 是nonpointer isa extra_rc++
uintptr_t carry;
newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry); // extra_rc++
if (slowpath(carry)) {
// newisa.extra_rc++ overflowed
if (!handleOverflow) {
ClearExclusive(&isa.bits);
return rootRetain_overflow(tryRetain);
}
// 超出以后一半存到散列表中,一半放在extra_rc 而且处理isa的extra_rc标志位和has_sidetable_rc
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)));
if (slowpath(transcribeToSideTable)) {
sidetable_addExtraRC_nolock(RC_HALF);
}
if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock();
return (id)this;
}
复制代码
进入到rootRetain
会进行以下操做:ui
isTaggedPointer
,TaggedPointer
是不须要维护引用计数的,直接返回。TaggedPointer
,就获取对象的isa
,判断是否是nonpointer isa
nonpointer isa
,交给散列表处理,对引用计数进行++
操做,而后返回deallocating
,是的话直接返回nonpointer isa
,对isa
的标志位extra_rc
执行++
操做extra_rc
能存储的范围了,就将其中的一半存在extra_rc
,并把has_sidetable_rc
标志位置为1。而后拷贝另一半放入散列表进行保存。散列表是多张表,因为性能和安全的考虑,是多张而不是一张,可是多张并非每一个对象就一张。散列表的存储引用计数的方式以下,也就是对存储的引用计数进行++
操做:this
bool
objc_object::sidetable_tryRetain()
{
SideTable& table = SideTables()[this];
bool result = true;
RefcountMap::iterator it = table.refcnts.find(this);
if (it == table.refcnts.end()) {
table.refcnts[this] = SIDE_TABLE_RC_ONE;
} else if (it->second & SIDE_TABLE_DEALLOCATING) {
result = false;
} else if (! (it->second & SIDE_TABLE_RC_PINNED)) {
it->second += SIDE_TABLE_RC_ONE;
}
return result;
}
id
objc_object::sidetable_retain()
{
#if SUPPORT_NONPOINTER_ISA
assert(!isa.nonpointer);
#endif
SideTable& table = SideTables()[this];
table.lock();
size_t& refcntStorage = table.refcnts[this];
if (! (refcntStorage & SIDE_TABLE_RC_PINNED)) {
refcntStorage += SIDE_TABLE_RC_ONE;
}
table.unlock();
return (id)this;
}
复制代码
release
retain
是引用计数+1
,而release
是引用计数-1
,流程是相辅相成的。调用release
仍是会进入到objc_release
方法。spa
void
objc_release(id obj)
{
if (!obj) return;
if (obj->isTaggedPointer()) return;
return obj->release();
}
inline void
objc_object::release()
{
assert(!isTaggedPointer());
if (fastpath(!ISA()->hasCustomRR())) {
rootRelease();
return;
}
((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_release);
}
复制代码
能够看到,调用objc_release
也会先判断是不是isTaggedPointer
,若是是就直接返回。接着会判断对象没有自定义retain/release
方法就会调用rootRelease
,不然就经过objc_msgSend
发送SEL_release
消息。3d
bool
objc_object::rootRelease()
{
return rootRelease(true, false);
}
objc_object::rootRelease(bool performDealloc, bool handleUnderflow)
{
// isTaggedPointer 不作处理 直接返回
if (isTaggedPointer()) return false;
bool sideTableLocked = false;
isa_t oldisa;
isa_t newisa;
retry:
do {
oldisa = LoadExclusive(&isa.bits);
newisa = oldisa;
if (slowpath(!newisa.nonpointer)) {
// 不是nonpointer_isa 对散列表中的引用计数进行处理
ClearExclusive(&isa.bits);
if (sideTableLocked) sidetable_unlock();
return sidetable_release(performDealloc);
}
// nonpointer_isa 对isa的extra_rc 进行--操做
uintptr_t carry;
newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry);
if (slowpath(carry)) {
// 若是不够减,则须要去散列表的引用计数表中借位
goto underflow;
}
} while (slowpath(!StoreReleaseExclusive(&isa.bits,
oldisa.bits, newisa.bits)));
if (slowpath(sideTableLocked)) sidetable_unlock();
return false;
underflow:
newisa = oldisa;
// 此时isa的extra_rc已经清零 没有计数了
// isa的散列表引用标志位有值
if (slowpath(newisa.has_sidetable_rc)) {
if (!handleUnderflow) {
ClearExclusive(&isa.bits);
// 递归调用
return rootRelease_underflow(performDealloc);
}
if (!sideTableLocked) {
ClearExclusive(&isa.bits);
sidetable_lock();
sideTableLocked = true;
// 散列表没加锁,加锁 递归
goto retry;
}
// 把散列表里存储的引用计数取出来
size_t borrowed = sidetable_subExtraRC_nolock(RC_HALF);
// 散列表中的引用计数若是大于0
if (borrowed > 0) {
// 对散列表的引用计数作--操做 而后存入isa的extra_rc
newisa.extra_rc = borrowed - 1;
bool stored = StoreReleaseExclusive(&isa.bits,
oldisa.bits, newisa.bits);
if (!stored) {
// 若是没有成功的存入 isa的extra_rc 那就再存一遍
......
}
if (!stored) {
// 二次存入仍是没有成功 就把数据放回到散列表的引用计数表
sidetable_addExtraRC_nolock(borrowed);
goto retry;
}
// 从散列表借位--成功
sidetable_unlock();
return false;
}
else {
// 散列表的引用计数也是空的
}
}
// isa 没有在deallocating中 那抛出异常
if (slowpath(newisa.deallocating)) {
ClearExclusive(&isa.bits);
if (sideTableLocked) sidetable_unlock();
return overrelease_error();
}
// 将isa置为deallocating,而后再来递归一遍
newisa.deallocating = true;
if (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)) goto retry;
if (slowpath(sideTableLocked)) sidetable_unlock();
__sync_synchronize();
if (performDealloc) {
// 发送一个SEL_dealloc的消息
((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
}
return true;
}
复制代码
梳理一下,进入到rootRelease
以后的流程以下:
isTaggedPointer
,TaggedPointer
是不须要维护引用计数的,直接返回。TaggedPointer
,就获取对象的isa
,判断是否是nonpointer isa
nonpointer isa
,交给散列表处理,对引用计数进行--
操做,若是散列表的引用计数清零,就须要对该对象发送SEL_dealloc
信息,执行dealloc
操做,而后返回。nonpointer_isa
就对isa
的extra_rc
进行--
操做,当extra_rc
计数为0,不够减的时候,就须要从散列表的引用计数表借位减。isa
的has_sidetable_rc
是否有值,有值就进行第6步,没有值就进行第8步SEL_dealloc
信息,执行dealloc
操做。isa
的extra_rc
,返回。若是没有存入成功,则进行2次递归存储,若是仍是没有成功,就将计数存入散列表,继续进行一次递归操做。isa
是否正处于deallocating
,若是没有那就抛出异常。SEL_dealloc
信息,执行dealloc
操做。散列表的引用计数表release
引用计数的操做:
uintptr_t
objc_object::sidetable_release(bool performDealloc)
{
SideTable& table = SideTables()[this];
bool do_dealloc = false;
table.lock();
RefcountMap::iterator it = table.refcnts.find(this);
if (it == table.refcnts.end()) {
do_dealloc = true;
table.refcnts[this] = SIDE_TABLE_DEALLOCATING;
} else if (it->second < SIDE_TABLE_DEALLOCATING) {
do_dealloc = true;
it->second |= SIDE_TABLE_DEALLOCATING;
} else if (! (it->second & SIDE_TABLE_RC_PINNED)) {
it->second -= SIDE_TABLE_RC_ONE;
}
table.unlock();
if (do_dealloc && performDealloc) {
((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
}
return do_dealloc;
}
复制代码
dealloc
当页面销毁或者对象销毁的时候就会进入dealloc
方法进行相关的处理。
- (void)dealloc {
_objc_rootDealloc(self);
}
void
_objc_rootDealloc(id obj)
{
assert(obj);
obj->rootDealloc();
}
inline void
objc_object::rootDealloc()
{
// TaggedPointer 不用处理引用计数
if (isTaggedPointer()) return; // fixme necessary?
// 是nonpointer
// 没有弱引用表、没有关联对象、没有c++析构器、没有散列表引用计数
// 直接释放
if (fastpath(isa.nonpointer &&
!isa.weakly_referenced &&
!isa.has_assoc &&
!isa.has_cxx_dtor &&
!isa.has_sidetable_rc))
{
assert(!sidetable_present());
free(this);
}
else {
object_dispose((id)this);
}
}
id
object_dispose(id obj)
{
if (!obj) return nil;
objc_destructInstance(obj);
free(obj);
return nil;
}
void *objc_destructInstance(id obj)
{
if (obj) {
// Read all of the flags at once for performance.
bool cxx = obj->hasCxxDtor();
bool assoc = obj->hasAssociatedObjects();
// 存在c++析构函数 调用析构
if (cxx) object_cxxDestruct(obj);
// 存在关联对象 删除关联对象
if (assoc) _object_remove_assocations(obj);
obj->clearDeallocating();
}
return obj;
}
inline void
objc_object::clearDeallocating()
{
if (slowpath(!isa.nonpointer)) {
// 非non-pointer isa
sidetable_clearDeallocating();
}
else if (slowpath(isa.weakly_referenced || isa.has_sidetable_rc)) {
// non-pointer isa
clearDeallocating_slow();
}
assert(!sidetable_present());
}
void
objc_object::sidetable_clearDeallocating()
{
SideTable& table = SideTables()[this];
table.lock();
RefcountMap::iterator it = table.refcnts.find(this);
if (it != table.refcnts.end()) {
if (it->second & SIDE_TABLE_WEAKLY_REFERENCED) {
// 清除弱引用表
weak_clear_no_lock(&table.weak_table, (id)this);
}
// 清除散列表中的引用计数表的相关信息
table.refcnts.erase(it);
}
table.unlock();
}
void
objc_object::clearDeallocating_slow()
{
assert(isa.nonpointer && (isa.weakly_referenced || isa.has_sidetable_rc));
SideTable& table = SideTables()[this];
table.lock();
// 清除弱引用表
if (isa.weakly_referenced) {
weak_clear_no_lock(&table.weak_table, (id)this);
}
// 清除散列表中的引用计数表的相关信息
if (isa.has_sidetable_rc) {
table.refcnts.erase(this);
}
table.unlock();
}
复制代码
总结一下,调用dealloc
方法的流程:
TaggedPointer
,则不用处理引用计数,返回nonpointer_isa
,且没有弱引用表、没有关联对象、没有c++析构器、没有散列表引用计数,那就直接释放,不然进入第3步,调用object_dispose()
objc_destructInstance(obj)
,而后在调用free(obj)
objc_destructInstance()
方法中,判断若是存在cxx
析构器则须要调用析构方法,若是存在关联对象须要删除关联对象。弱引用表的释放详见weak
原理,此处就不赘述。
retain
流程release
流程dealloc
流程