内存的五大区
以4GB的内存空间为例:通常只是用到了3GB的内存(范围0xc0000000-0x00400000)。
内存地址从高到低为内核区、栈区、堆区、数据段、代码段已经保留区。
通常的五大区就是内核区和保留区之间的内存地址。
由高到低分别是:
- 栈区(stack) :栈区地址由上向下增长,栈区速度较快但是较小。
- 堆区(heap) :堆区地址由下向上增长。
- 未初始化数据(.bss):未初始化的全局变量、静态变量
- 已初始化数据(.data):初始化的全局变量、静态变量
- 代码段(.text):程序代码
内存管理方案
TaggedPointer:小对象-NSNumber,NSDate
NONPOINTER_ISA:非指针型isa
散列表:引用计数表,弱引用表
objc_release(id obj)
{
if (!obj) return;
if (obj->isTaggedPointer()) return;
return obj->release();
}
在iOS10.14开始出现了新的ISA优化,在_readImages模块的时候,增加了一步TaggedPointer
优化。
TaggedPointer
对象不需要引用计数的处理,不需要isa指针的那套流程效率更高。
TaggedPointer介绍
MRC&ARC
- 引用计数如何存放
- retain是如何处理的
首先看看retain
内部
objc_object::retain()
{
assert(!isTaggedPointer());
if (fastpath(!ISA()->hasCustomRR())) {
return rootRetain();
}
return ((id(*)(objc_object *, SEL))objc_msgSend)(this, SEL_retain);
}
一个是快速流程,一个慢速的msgSend流程的retain。
retain的基本思路是:
如果isa是nonpointer
类型,那么直接获取bits
中对应的extra_rc
,对引用计数进行增加,如果extra_rc
溢出了,大于一定的计数,那么就会使用sidetable
引用技术表,根据地址值通过哈希函数获取一个下标,从全局的sidetables
中获取到指定下标的sidetable
,然后把一半的引用计数放入计数表中extra_rc
依旧保留一半的引用计数。
如果isa是非nonpointer
类型,那么直接吧引用计数放入sidetable
中。
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;
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;
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);
}
// 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)));
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;
}
retainCount
- 如果isa是
nonpointer
类型,那么引用计数的计算就是:
1 + extra_rc + sidetable中的引用计数 - 如果isa是非
nonpointer
类型,就直接返回sidetable中的引用计数。
那么,nonpointer
isa在alloc出来的时候,你不管怎么查看引用计数,最少都是1,这是默认值。
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();
}
dealloc
判断isa是否存在相关的一些标志位,没有则说明是普通的isa。
如果存在相关标志位,那么就会调用相关的一些析构函数。
比如cxx、weak表、引用计数表、关联对象等。
inline void
objc_object::rootDealloc()
{
if (isTaggedPointer()) return; // fixme necessary?
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);
}
}
void *objc_destructInstance(id obj)
{
if (obj) {
// Read all of the flags at once for performance.
bool cxx = obj->hasCxxDtor();
bool assoc = obj->hasAssociatedObjects();
// This order is important.
if (cxx) object_cxxDestruct(obj);
if (assoc) _object_remove_assocations(obj);
obj->clearDeallocating();
}
return obj;
}
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();
// This order is important.
if (cxx) object_cxxDestruct(obj);
if (assoc) _object_remove_assocations(obj);
obj->clearDeallocating();
}
return obj;
}
循环引用、强引用
在开发中经常使用timer
定时器,如果在VC销毁的时候不把timer关掉,对象是不会销毁的。
因为官方文档说了,timer
会对传递进来的target
直接强引用,那么就会出现self-->timer-->self 的循环引用。
这里如果使用weakself
也不能解决问题。
这里是因为,这个持有关系不是简单持有,是因为:
runloop-->timer-->weakself -->self
self.timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(sel) userInfo:nil repeats:NO];
网友评论