內(nèi)存布局

- stack(棧區(qū)): 方法調(diào)用
- heap(堆區(qū)):通過(guò)
alloc等分配的對(duì)象 - bss:未初始化的全局變量或靜態(tài)變量等。
- data:已初始化的全局變量等。
- text:程序的代碼段
內(nèi)存管理方案
iOS是如何對(duì)內(nèi)存進(jìn)行管理的?
TaggedPointer:對(duì)一些小對(duì)象如
NSNumber等-
NONPOINTER_ISA: 對(duì)于64位架構(gòu)下的應(yīng)用程序
在64位架構(gòu)下,isa指針占用64位bit,實(shí)際有32位或者40位就夠用了,剩余的實(shí)際上是浪費(fèi)的,蘋果為了提高內(nèi)存利用率,在這些剩余的bit位當(dāng)中,存儲(chǔ)了一些關(guān)于內(nèi)存管理的相關(guān)數(shù)據(jù)內(nèi)容,所以稱為非指針型的isa
-
散列表
散列表是一個(gè)復(fù)雜的數(shù)據(jù)結(jié)構(gòu),其中包含了應(yīng)用計(jì)數(shù)表和弱引用計(jì)數(shù)表。
NONPOINTER_ISA結(jié)構(gòu)
arm64架構(gòu)


第0號(hào)位是
indexed的標(biāo)志位,如果這個(gè)位置是0,代表的是我們使用的isa指針只是一個(gè)純的isa指針,它里面的內(nèi)容就直接代表了當(dāng)前對(duì)象的類對(duì)象的地址;如果這個(gè)位置是1,就代表這個(gè)isa指針里面存儲(chǔ)的不僅是他的類對(duì)象的地址,而且還有一些內(nèi)存管理方面的數(shù)據(jù)。第1號(hào)位
has_assoc是表示當(dāng)前對(duì)象是否有關(guān)聯(lián)對(duì)象,0沒(méi)有,1有。第2位
has_cxx_dtor,表示的是當(dāng)前對(duì)象是否有使用到C++相關(guān)的一些代碼,或者C++語(yǔ)言方面的一些內(nèi)容。在ARC中也可以通過(guò)這個(gè)標(biāo)志位,來(lái)表示有些對(duì)象是通過(guò)ARC來(lái)進(jìn)行內(nèi)存管理的。后面的3-35位
shiftcls,表示當(dāng)前對(duì)象的類對(duì)象指針地址。后面的6位是一個(gè)
magic字段后面是一位
weakly_referenced,標(biāo)識(shí)這個(gè)對(duì)象是否有相應(yīng)的弱引用指針。deallocating,表示的是當(dāng)前對(duì)象是否在進(jìn)行dealloc操作has_sidetable_rc,表示的是當(dāng)前這個(gè)isa指針當(dāng)中,如果所存儲(chǔ)的引用計(jì)數(shù)已經(jīng)達(dá)到了上限的話,需要外掛一個(gè)sidetable數(shù)據(jù)結(jié)構(gòu),去存儲(chǔ)相關(guān)的引用計(jì)數(shù)內(nèi)容(也就是散列表)extra_rc額外的引用計(jì)數(shù),當(dāng)我們引用計(jì)數(shù)在一個(gè)很小的值得范圍之內(nèi)就會(huì)存到isa指針當(dāng)中,而不是由單獨(dú)的引用計(jì)數(shù)表去存他的引用計(jì)數(shù)。
散列表方式
SideTables()源碼
static StripedMap<SideTable>& SideTables() {
return *reinterpret_cast<StripedMap<SideTable>*>(SideTableBuf);
}
SideTables()結(jié)構(gòu)

side tables實(shí)際上是一個(gè)hash表,通過(guò)一個(gè)對(duì)象指針,找到他對(duì)應(yīng)的引用計(jì)數(shù)表,或弱引用表。
Side Table
SideTable源碼
struct SideTable {
spinlock_t slock;
RefcountMap refcnts;
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結(jié)構(gòu)

為什么不是一個(gè)side table?

假如說(shuō)只有一張side table,相當(dāng)于我們?cè)趦?nèi)存當(dāng)中分配的所有對(duì)象的引用計(jì)數(shù)或者說(shuō)弱引用存儲(chǔ)都放在一張大表當(dāng)中,這個(gè)時(shí)候如果我們要操作某一個(gè)對(duì)象的引用計(jì)數(shù)值進(jìn)行修改,比如說(shuō)進(jìn)行加1或減1的操作的話,由于所有的對(duì)象可能是在不同的線程當(dāng)中去分配創(chuàng)建的,包括調(diào)用他們的release,retain等方法,也可能是在不同的線程當(dāng)中進(jìn)行操作;這個(gè)時(shí)候?qū)σ环N表進(jìn)行操作的時(shí)候,需要進(jìn)行加鎖處理,才能保證對(duì)于數(shù)據(jù)的訪問(wèn)安全,在這個(gè)過(guò)程中就存在了一個(gè)效率問(wèn)題。比如說(shuō)用戶的內(nèi)存空間一共有4GB,那么可能分配出成千上百萬(wàn)個(gè)內(nèi)存對(duì)象,如果說(shuō)每一個(gè)對(duì)象在對(duì)他進(jìn)行內(nèi)存引用計(jì)數(shù)的改變的時(shí)候,都操作這張表很顯然就會(huì)有效率的問(wèn)題。如果說(shuō)已經(jīng)又一個(gè)對(duì)象在操作這張表,下一個(gè)對(duì)象就要等他操作完,把鎖釋放之后再進(jìn)行操作,這效率就會(huì)太低了。

系統(tǒng)為了解決效率問(wèn)題,引入了分離鎖的技術(shù)方案。我們可以把內(nèi)存對(duì)象所對(duì)應(yīng)的引用計(jì)數(shù)表,可以分拆成多個(gè)部分。比如說(shuō)分拆成8個(gè),需要對(duì)8個(gè)表分別加鎖。當(dāng)A和B同時(shí)進(jìn)行引用計(jì)數(shù)操作的話可以進(jìn)行并發(fā)操作,如果是一張表他們需要進(jìn)行順序操作。很明顯分離鎖可以提高訪問(wèn)效率。
怎樣實(shí)現(xiàn)快速分流?
快速分流指通過(guò)一個(gè)對(duì)象的指針如何快速定位到它屬于那張side table 表當(dāng)中。

side tables的本質(zhì)是一張Hash表。這張hash表當(dāng)中,可能有64張具體的side table 存儲(chǔ)不同對(duì)象的引用計(jì)數(shù)表和弱引用表。
自旋鎖 Spinlock_t
-
Spinlock_t是"忙等"的鎖。
如果當(dāng)前鎖已被其他線程獲取,那么當(dāng)前線程會(huì)不斷的探測(cè)這個(gè)鎖是否有被釋放,如果釋放掉,自己第一時(shí)間去獲取這個(gè)鎖。所以說(shuō)自旋鎖是一種忙等的鎖。獲取不到鎖的時(shí)候,他會(huì)他自己的線程阻塞休眠,然后等到其他線程釋放這個(gè)鎖的時(shí)候來(lái)喚醒當(dāng)前線程。
適用于輕量訪問(wèn)。
引用計(jì)數(shù)表RefcountMap
引用計(jì)數(shù)表實(shí)際上是一個(gè)hash表,我們可以通過(guò)指針來(lái)找到對(duì)應(yīng)對(duì)象的引用天計(jì)數(shù),這一過(guò)程實(shí)際上也是hash查找(使用hash查找是為了提高查找效率)。

插入和獲取是通過(guò)同一個(gè)hash函數(shù)完成,避免了遞歸查找和for循環(huán)遍歷
size_t內(nèi)存分配

- 第一個(gè)二進(jìn)制位表示的是
weakly_referenced,對(duì)象是否有弱引用,0沒(méi)有,1有。 - 第二位
deallocating表示當(dāng)前對(duì)象是否處于dealloc中 - 后面(RC)存儲(chǔ)的是對(duì)象的實(shí)際引用計(jì)數(shù)值,在實(shí)際計(jì)算這個(gè)引用計(jì)數(shù)值,需要向右偏移兩位,因?yàn)楹竺鎯晌恍枰サ簟?/li>
弱引用表weak_table_t
weak_table_t實(shí)際上也是一個(gè)hash表.

weak_entry_t實(shí)際上是一個(gè)結(jié)構(gòu)體數(shù)組。結(jié)構(gòu)體數(shù)組存儲(chǔ)的是每一個(gè)的弱引用指針,也就是代碼當(dāng)中定義的__weak id obj,obj內(nèi)存地址即指針就存儲(chǔ)在weak_entry_t
MRC & ARC
MRC 手動(dòng)引用計(jì)數(shù)
-
alloc: 用來(lái)分配一個(gè)對(duì)象的內(nèi)存空間。 -
retain:對(duì)一個(gè)對(duì)象的引用計(jì)數(shù)加1; -
release:對(duì)一個(gè)對(duì)象的引用計(jì)數(shù)減1; -
retainCount:獲取當(dāng)前對(duì)象的引用計(jì)數(shù)值 -
autorelease:如果調(diào)用了一個(gè)對(duì)象的autorelease方法,當(dāng)前這個(gè)對(duì)象會(huì)在autoreleasepool結(jié)束的時(shí)候,調(diào)用他的release操作進(jìn)行引用計(jì)數(shù)減1. -
dealloc:在MRC當(dāng)中調(diào)用dealloc方法需要顯式調(diào)用[super dealloc]來(lái)釋放或廢棄父類的相關(guān)成員變量。
ARC 自動(dòng)引用計(jì)數(shù)
- ARC是LLVM和Runtime協(xié)作來(lái)進(jìn)行自動(dòng)引用計(jì)數(shù)管理;
- ARC中禁止手動(dòng)調(diào)用
retain,release,retainCount,dealloc,并且在ARC中可以重寫某個(gè)對(duì)象的dealloc方法,但是不能再dealloc方法當(dāng)中,顯示調(diào)用[super dealloc]; - ARC中新增了
weak,strong屬性關(guān)鍵字。
ARC實(shí)際是由編譯期自動(dòng)為我們插入
retain和release操作之外,還需要runtime的功能進(jìn)行支持,然后由編譯器和Runtime共同協(xié)作才能組成ARC的全部功能。
引用計(jì)數(shù)管理
實(shí)現(xiàn)原理分析
- alloc
- retain
- release
- retainCount
- dealloc
alloc實(shí)現(xiàn)
- 經(jīng)過(guò)一系列調(diào)用,最終調(diào)用了C函數(shù)
calloc - 此時(shí)并沒(méi)有設(shè)置引用計(jì)數(shù)為1
retain實(shí)現(xiàn)
id
objc_object::sidetable_retain()
{
#if SUPPORT_NONPOINTER_ISA
assert(!isa.nonpointer);
#endif
//hash查找SideTable
SideTable& table = SideTables()[this];
//SideTable加鎖
table.lock();
//hash查找引用計(jì)數(shù)值
size_t& refcntStorage = table.refcnts[this];
if (! (refcntStorage & SIDE_TABLE_RC_PINNED)) {
//引用計(jì)數(shù)加1
//#define SIDE_TABLE_RC_ONE (1UL<<2)
refcntStorage += SIDE_TABLE_RC_ONE;
}
table.unlock();
return (id)this;
}
- 通過(guò)當(dāng)前對(duì)象的指針
this,經(jīng)過(guò)hash函數(shù)的計(jì)算,可以快速的SideTables當(dāng)中找到(hash查找)它對(duì)應(yīng)的SideTable。 - 然后在
SideTable當(dāng)中獲取應(yīng)用計(jì)數(shù)map這個(gè)成員變量,通過(guò)對(duì)象的指針this,在SideTable的引用計(jì)數(shù)表中獲取(hash查找)當(dāng)前當(dāng)前對(duì)象的引用計(jì)數(shù)值。 - 經(jīng)過(guò)一定的條件判斷之后,引用計(jì)數(shù)加1。
引用計(jì)數(shù)加1,實(shí)際是加上了偏移量對(duì)應(yīng)的操作,這個(gè)偏移量是4,反應(yīng)出來(lái)的結(jié)果是加1,因?yàn)?code>size_t64位,前兩位不是存儲(chǔ)引用計(jì)數(shù),所以需要向左偏移兩位操作
1UL<<2
release實(shí)現(xiàn)
uintptr_t
objc_object::sidetable_release(bool performDealloc)
{
#if SUPPORT_NONPOINTER_ISA
assert(!isa.nonpointer);
#endif
//hash查找SideTable
SideTable& table = SideTables()[this];
bool do_dealloc = false;
//加鎖
table.lock();
//根據(jù)當(dāng)前對(duì)象指針,訪問(wèn)table的應(yīng)用計(jì)數(shù)表
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) {
// SIDE_TABLE_WEAKLY_REFERENCED may be set. Don't change it.
do_dealloc = true;
it->second |= SIDE_TABLE_DEALLOCATING;
} else if (! (it->second & SIDE_TABLE_RC_PINNED)) {
//引用計(jì)數(shù)減1操作
it->second -= SIDE_TABLE_RC_ONE;
}
table.unlock();
if (do_dealloc && performDealloc) {
((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
}
return do_dealloc;
}
- 通過(guò)當(dāng)前對(duì)象的指針
this,經(jīng)過(guò)hash函數(shù)的計(jì)算,可以快速的SideTables當(dāng)中找到(hash查找)它對(duì)應(yīng)的SideTable。 - 根據(jù)當(dāng)前對(duì)象指針,訪問(wèn)table的應(yīng)用計(jì)數(shù)表
- 找到對(duì)應(yīng)的值進(jìn)行引用計(jì)數(shù)減1操作
retainCount實(shí)現(xiàn)
uintptr_t
objc_object::sidetable_retainCount()
{
//hash查找SideTable
SideTable& table = SideTables()[this];
//聲明局部變量賦值為1
size_t refcnt_result = 1;
//加鎖
table.lock();
//根據(jù)當(dāng)前對(duì)象指針,訪問(wèn)table的應(yīng)用計(jì)數(shù)表
RefcountMap::iterator it = table.refcnts.find(this);
if (it != table.refcnts.end()) {
// this is valid for SIDE_TABLE_RC_PINNED too
//查找結(jié)果向右位移2位,再加上局部變量的值
refcnt_result += it->second >> SIDE_TABLE_RC_SHIFT;
}
table.unlock();
return refcnt_result;
}
通過(guò)當(dāng)前對(duì)象的指針
this,經(jīng)過(guò)hash函數(shù)的計(jì)算,可以快速的SideTables當(dāng)中找到(hash查找)它對(duì)應(yīng)的SideTable。根據(jù)當(dāng)前對(duì)象指針,訪問(wèn)table的應(yīng)用計(jì)數(shù)表
-
找到對(duì)應(yīng)的值向右位移2位,再加上局部變量的值1
這就是alloc操作之后,引用計(jì)數(shù)沒(méi)有變化,但retainCount獲取的值是1的原因
dealloc實(shí)現(xiàn)源碼
// Replaced by NSZombies
- (void)dealloc {
_objc_rootDealloc(self);
}
void
_objc_rootDealloc(id obj)
{
assert(obj);
obj->rootDealloc();
}
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);
}
}
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;
}
inline void
objc_object::clearDeallocating()
{
if (slowpath(!isa.nonpointer)) {
// Slow path for raw pointer 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.
clearDeallocating_slow();
}
assert(!sidetable_present());
}
void
objc_object::sidetable_clearDeallocating()
{
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();
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();
}
NEVER_INLINE 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();
}
void
objc_object::sidetable_clearDeallocating()
{
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();
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();
}
dealloc實(shí)現(xiàn)流程圖

object_dispose()實(shí)現(xiàn)

objc_destructInstance()實(shí)現(xiàn)

clearDeallocating()實(shí)現(xiàn)

弱引用管理

id
objc_initWeak(id *location, id newObj)
{
if (!newObj) {
*location = nil;
return nil;
}
return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
(location, (objc_object*)newObj);
}
添加weak變量的弱引用實(shí)現(xiàn)

當(dāng)一個(gè)對(duì)象被釋放或者廢棄之后,weak變量怎樣處理的?
/**
* Called by dealloc; nils out all weak pointers that point to the
* provided object so that they can no longer be used.
*
* @param weak_table
* @param referent The object being deallocated.
*/
void
weak_clear_no_lock(weak_table_t *weak_table, id referent_id)
{
objc_object *referent = (objc_object *)referent_id;
weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);
if (entry == nil) {
/// XXX shouldn't happen, but does with mismatched CF/objc
//printf("XXX no entry for clear deallocating %p\n", referent);
return;
}
// zero out references
weak_referrer_t *referrers;
size_t count;
if (entry->out_of_line()) {
referrers = entry->referrers;
count = TABLE_SIZE(entry);
}
else {
referrers = entry->inline_referrers;
count = WEAK_INLINE_COUNT;
}
for (size_t i = 0; i < count; ++i) {
//referrers取到弱引用指針的所有對(duì)應(yīng)的數(shù)組列表
objc_object **referrer = referrers[I];
if (referrer) {//如果referrer即弱引用指針存在
if (*referrer == referent) { //如果弱引用指針對(duì)應(yīng)的是被廢棄的對(duì)象的話,就將指針置為nil
*referrer = nil;
}
else if (*referrer) {
_objc_inform("__weak variable at %p holds %p instead of %p. "
"This is probably incorrect use of "
"objc_storeWeak() and objc_loadWeak(). "
"Break on objc_weak_error to debug.\n",
referrer, (void*)*referrer, (void*)referent);
objc_weak_error();
}
}
}
weak_entry_remove(weak_table, entry);
}
當(dāng)一個(gè)對(duì)象被dealloc之后,內(nèi)部實(shí)現(xiàn)當(dāng)中會(huì)去調(diào)用
weak_clear_no_lock()函數(shù),函數(shù)實(shí)現(xiàn)內(nèi)部會(huì)根據(jù)弱引用指針查找弱引用表把當(dāng)前對(duì)象相對(duì)應(yīng)的弱引用拿出來(lái),然后遍歷數(shù)組的所有弱引用指針,分別置為nil
自動(dòng)釋放池
編譯期會(huì)將代碼塊@autoreleasepool{}改寫為:
void *ctx = objc_autoreleasePoolPush();{}中的代碼
-
objc_autoreleasePoolPop(ctx);一次pop實(shí)際上相當(dāng)于一次批量的pop操作
void *
objc_autoreleasePoolPush(void)
{
return AutoreleasePoolPage::push();
}
void
objc_autoreleasePoolPop(void *ctxt)
{
AutoreleasePoolPage::pop(ctxt);
}
自動(dòng)釋放池的數(shù)據(jù)結(jié)構(gòu)
- 是以棧為節(jié)點(diǎn)通過(guò)雙向鏈表的形式組合而成。(什么是自動(dòng)釋放池/自動(dòng)釋放池的數(shù)據(jù)結(jié)構(gòu)是怎樣的?)
- 是和線程一一對(duì)應(yīng)的
雙向鏈表

棧

AutoreleasePoolPage類源碼
class AutoreleasePoolPage
{
// EMPTY_POOL_PLACEHOLDER is stored in TLS when exactly one pool is
// pushed and it has never contained any objects. This saves memory
// when the top level (i.e. libdispatch) pushes and pops pools but
// never uses them.
# define EMPTY_POOL_PLACEHOLDER ((id*)1)
# define POOL_BOUNDARY nil
static pthread_key_t const key = AUTORELEASE_POOL_KEY;
static uint8_t const SCRIBBLE = 0xA3; // 0xA3A3A3A3 after releasing
static size_t const SIZE =
#if PROTECT_AUTORELEASEPOOL
PAGE_MAX_SIZE; // must be multiple of vm page size
#else
PAGE_MAX_SIZE; // size and alignment, power of 2
#endif
static size_t const COUNT = SIZE / sizeof(id);
magic_t const magic;
id *next;//指向當(dāng)前棧中下一個(gè)可填充的位置
pthread_t const thread; //線程
AutoreleasePoolPage * const parent;//雙向鏈表的父指針
AutoreleasePoolPage *child;//雙向鏈表的子指針
uint32_t const depth;
uint32_t hiwat;
// SIZE-sizeof(*this) bytes of contents follow
static void * operator new(size_t size) {
return malloc_zone_memalign(malloc_default_zone(), SIZE, SIZE);
}
static void operator delete(void * p) {
return free(p);
}
.....
}
AutoreleasePoolPage結(jié)構(gòu)

AutoreleasePoolPage::push

[obj autorelease]

AutoreleasePoolPage::pop
- 根據(jù)傳入的哨兵對(duì)象找到對(duì)應(yīng)位置。
- 給上次push操作之后添加的對(duì)象依次發(fā)送release消息。
- 回退next指針到正確位置。


總結(jié)
- 當(dāng)每次runloop將要結(jié)束的時(shí)候調(diào)用
AutoreleasePoolPage::pop() - 多層嵌套就是多次插入哨兵對(duì)象。
- 在for循環(huán)中alloc圖片數(shù)據(jù)等內(nèi)存消耗較大的場(chǎng)景手動(dòng)插入
autoreleasePool
- (void)viewDidLoad
{
[super viewDidLoad];
NSMutableArray *array = [NSMutableArray array];
NSLog(@"%@",array);
}
上面array的內(nèi)存在什么時(shí)候釋放的?
當(dāng)每次runloop將要結(jié)束的時(shí)候,都會(huì)對(duì)前一次創(chuàng)建的AutoreleasePool調(diào)用
AutoreleasePoolPage::pop()操作,同時(shí)會(huì)push進(jìn)來(lái)一個(gè)AutoreleasePool。所以array對(duì)象會(huì)在當(dāng)前runloop就要結(jié)束的時(shí)候調(diào)用AutoreleasePoolPage::pop()方法,把對(duì)應(yīng)的array對(duì)象,調(diào)用其release函數(shù)對(duì)其進(jìn)行釋放
AutoreleasePool的實(shí)現(xiàn)原理是怎樣的?
是以棧為節(jié)點(diǎn)通過(guò)雙向鏈表的形式組合而成的數(shù)據(jù)結(jié)構(gòu)
AutoreleasePool為何可以嵌套使用?
多層嵌套就是多次插入哨兵對(duì)象。在我們每次創(chuàng)建代碼塊
@autoreleasepool{},系統(tǒng)就會(huì)為我們進(jìn)行哨兵對(duì)象的插入,完成新的AutoreleasePool的創(chuàng)建,實(shí)際上也是創(chuàng)建了一個(gè)AutoreleasePoolPage,假如當(dāng)前AutoreleasePoolPage沒(méi)有滿的話,就不用創(chuàng)建AutoreleasePoolPage。所以新創(chuàng)建的AutoreleasePool底層就是插入一個(gè)哨兵對(duì)象,所以可以多層嵌套。
循環(huán)引用
三種循環(huán)引用
- 自循環(huán)引用
- 相互循環(huán)引用
- 多循環(huán)引用
自循環(huán)引用

相互循環(huán)引用

多循環(huán)引用

循環(huán)引用考點(diǎn)
- 代理
- Block
- NSTimer
- 大環(huán)引用
如何破除循環(huán)引用?
- 避免產(chǎn)生循環(huán)引用
- 在合適的時(shí)機(jī)手動(dòng)斷環(huán)
破除循環(huán)引用具體的解決方案都有哪些?
-
__weak破解 -
__block破解 -
__unsafe_unretained破解
__weak破解

__block破解
- MRC下,
__block修飾對(duì)象不會(huì)增加其引用計(jì)數(shù),避免了循環(huán)引用。 - ARC下,
__block修飾對(duì)象會(huì)被強(qiáng)引用,無(wú)法避免循環(huán)引用,需手動(dòng)解環(huán)。
__unsafe_unretained破解
- 修飾對(duì)象不會(huì)增加其引用計(jì)數(shù),避免了循環(huán)引用。
- 如果被修飾對(duì)象在某一時(shí)機(jī)被釋放,會(huì)產(chǎn)生懸垂指針!
循環(huán)引用示例
Block的使用示例。(參看Block的講解)
NSTimer使用示例。

#import "NSTimer+WeakTimer.h"
@interface TimerWeakObject : NSObject
@property (nonatomic, weak) id target;
@property (nonatomic, assign) SEL selector;
@property (nonatomic, weak) NSTimer *timer;
- (void)fire:(NSTimer *)timer;
@end
@implementation TimerWeakObject
- (void)fire:(NSTimer *)timer
{
if (self.target) {
if ([self.target respondsToSelector:self.selector]) {
[self.target performSelector:self.selector withObject:timer.userInfo];
}
}
else{
[self.timer invalidate];
}
}
@end
@implementation NSTimer (WeakTimer)
+ (NSTimer *)scheduledWeakTimerWithTimeInterval:(NSTimeInterval)interval
target:(id)aTarget
selector:(SEL)aSelector
userInfo:(id)userInfo
repeats:(BOOL)repeats
{
TimerWeakObject *object = [[TimerWeakObject alloc] init];
object.target = aTarget;
object.selector = aSelector;
object.timer = [NSTimer scheduledTimerWithTimeInterval:interval target:object selector:@selector(fire:) userInfo:userInfo repeats:repeats];
return object.timer;
}
@end
內(nèi)存管理面試總結(jié)
- 什么是ARC?
- 為什么weak指針指向的對(duì)象在廢棄之后會(huì)被自動(dòng)置為nil?
- 蘋果是如何實(shí)現(xiàn)AutoreleasePool的?
- 什么是循環(huán)引用?你遇到過(guò)哪些循環(huán)引用,是怎樣解決的?