iOS-內(nèi)存管理知識點整理

內(nèi)存管理.png

image.png

image.png

indexed標識isa是否僅僅為一個內(nèi)存指針,如果為1的話就僅是一個內(nèi)存指針,如果為0的話則意味著內(nèi)存的64位不僅僅用于存儲內(nèi)存指針

has_assoc代表該對象是否有關聯(lián)屬性
has_cxx_dtor代表對象是否有和c++相關的屬性
shiftcls代表對象實際的內(nèi)存地址
weakly_referenced代表對象是否有弱引用指向
deallocating標識對象是否正在被銷毀
has_sidetable_rc代表對象是否有額外的引用計數(shù)表
extra_rc代表對象的引用計數(shù)(當對象的引用計數(shù)很小的時候對象的引用計數(shù)就記錄在當前對象的isa指針中)


image.png
image.png

為什么不是一個sidetable呢??
如果只有一個表的話系統(tǒng)的操作對象的引用計數(shù)時其他的對象就在等待,這就降低了效率 。


image.png

使用64張table存儲的話就能實現(xiàn)并發(fā)操作。

image.png

自旋鎖是循環(huán)訪問的機制,只適用于輕量訪問的情況。

image.png

引用計數(shù)表的前兩位分別標識該對象是否有弱引用以及是否處于正在銷毀狀態(tài),所以計算引用計數(shù)時要向右偏移兩位

image.png

弱引用表是一張Hash表,key為當前對象的指針地址,vlaue為指向當前對象的弱引用對象鏈表

MRC和ARC的區(qū)別

image.png
image.png

ARC的本質是編譯器和Runtime協(xié)作的結果

image.png

引用計數(shù)的管理機制

image.png

alloc的時候并沒有將對象的引用計數(shù)設置為1,而是retain的初始值為1

image.png
objc_object::sidetable_retainCount()
{
    SideTable& table = SideTables()[this];

    size_t refcnt_result = 1;
    
    table.lock();
    RefcountMap::iterator it = table.refcnts.find(this);
    if (it != table.refcnts.end()) {
        // this is valid for SIDE_TABLE_RC_PINNED too
        refcnt_result += it->second >> SIDE_TABLE_RC_SHIFT;
    }
    table.unlock();
    return refcnt_result;
}

retainCount的實現(xiàn):先在sidetables表集合中根據(jù)對象的指針地址找到對應的sidetable,再sidetable中的通過hash查找找到對象對應的引用計數(shù),如果當前的表中對象是新建的話,此時hash表中的it->second為0,返回默認值累加值1.
如果不為0,則將對應數(shù)值向右偏移兩位后加1.

image.png
id
objc_object::sidetable_retain()
{
#if SUPPORT_NONPOINTER_ISA
    assert(!isa.indexed);
#endif
    SideTable& table = SideTables()[this];

    if (table.trylock()) {
        size_t& refcntStorage = table.refcnts[this];
        if (! (refcntStorage & SIDE_TABLE_RC_PINNED)) {
            refcntStorage += SIDE_TABLE_RC_ONE;
        }
        table.unlock();
        return (id)this;
    }
    return sidetable_retain_slow(table);
}

對象的調(diào)用dealloc后的銷毀過程

image.png

要判斷對象是否有isa指針,弱引用,關聯(lián)對象,c++相關和引用計數(shù)表并對應執(zhí)行銷毀操作。

image.png

object_dispose 實現(xiàn)

image.png

objc_destructInstance實現(xiàn)


image.png

銷毀c++和關聯(lián)對象后再銷毀對象的引用計數(shù)和弱引用

image.png

弱引用初始化時Runtime會調(diào)用obc_initWeak函數(shù)初始化一個新的weak指針指向對象,經(jīng)過objc_storeWeak調(diào)整參數(shù),最終由weak_register_no_lock創(chuàng)建弱引用鏈表存儲對象的指針。


image.png

image.png

清除weak變量并設置nil的過程如下


image.png

釋放時,調(diào)用sidetable_clearDeallocating函數(shù),然后調(diào)用weak_clear_no_lock()根據(jù)對象的地址獲取到為weak表中的指向該對象的弱指針數(shù)組,遍歷數(shù)組將數(shù)組中的所有項都設置為nil,最后把entry從weak表中刪除。

總結一下 添加weak變量中會調(diào)用 initWeak(),storeWeak(),weak_register_no_lock()函數(shù)。清除weak變量置nil會調(diào)用learDeallocating(),weak_clear_no_lock()函數(shù)。在添加和刪除的過程中都不斷的使用hash查找定位變量在weak表中的位置。

自動釋放池autoreleasePool

問題1.圖中的array是什么時候被釋放的??
在每次runloop開始都會調(diào)用一次autoreleasePoolPush操作,每次runloop結束的時候都會調(diào)用一次autoreleasePoolPop操作,array就是在pop操作的過程中被release釋放。


image.png

問題2.autoreleasePool的實現(xiàn)原理是什么??
問題3.autoreleasePool是如何實現(xiàn)嵌套使用的??
問題4.在實際開發(fā)過程中autoreleasePool的應用場景是怎樣的??

image.png

在實際的開發(fā)過程中@autoreleasePool{}會被改寫成下圖的代碼會在要執(zhí)行的代碼前后分別加上autoreleasePoolPush和autoreleasePoolPop

image.png
image.png
image.png

autoreleasePoolPage的本質是以棧為節(jié)點通過雙向鏈表的形式組合而成結構體,而且autoreleasePoolPage與具體的線程相關聯(lián)

image.png

autoreleasePoolPush操作是向autoreleasePoolPage棧中插入哨兵對象標識本次的autorelease開始存儲數(shù)據(jù)位置,autoreleasePoolPop首先找到相應的autoreleasePoolPage并對棧進行出棧操作,每出棧一個對象的同時會調(diào)用對象的release方法直到出棧至最上方的哨兵對象停止出棧。

image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png

NSTimer產(chǎn)生循環(huán)引用的原因和解決方案

[NSTimer scheduledTimerWithTimeInterval:interval target:object selector:@selector(fire:) userInfo:userInfo repeats:repeats];

NSTimer注冊到當前線程的runloop中后 當前的runloop會持有timer的強引用,同時timer強引用當前對象形成環(huán)引用。


image.png

解決方法:在timer和object中間添加中間對象,中間對象分別弱引用timer和object,在每次執(zhí)行timer的方法時都判斷oject是否存在,存在則調(diào)用方法不存在則消除timer


image.png
#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)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 1、內(nèi)存布局 stack:方法調(diào)用 heap:通過alloc等分配對象 bss:未初始化的全局變量等。 data:...
    AKyS佐毅閱讀 1,712評論 0 19
  • 前言 從我開始學習iOS的時候,身邊的朋友、網(wǎng)上的博客都告訴我iOS的內(nèi)存管理是依靠引用計數(shù)的,然后說引用計數(shù)大于...
    蓋世英雄_ix4n04閱讀 657評論 0 1
  • 文章目錄 一.內(nèi)存管理準則 二.屬性內(nèi)存管理修飾符全解析 三.block中的weak和strong 四.weak是...
    YouKnowZrx閱讀 1,108評論 5 10
  • iOS中內(nèi)存管理機制是開發(fā)中一項很重要的知識,了解iOS中內(nèi)存管理的規(guī)則不管是在開發(fā)中還是在學習中都能很大程度的幫...
    Horson19閱讀 2,000評論 0 7
  • 每日5000步√ 《捕捉兒童敏感期》放肆的語言 語言的發(fā)展是一個螺旋式的發(fā)展過程,每一個時期對語言的敏感點有所不同...
    因蒙而萌閱讀 232評論 0 0

友情鏈接更多精彩內(nèi)容