內(nèi)存管理

1、何檢測內(nèi)存泄漏

  • Memory Leaks
  • Alloctions
  • Analyse
  • Debug Memory Graph
  • MLeaksFinder

泄露的內(nèi)存主要有以下兩種:

  • Laek Memory 這種是忘記Release 操作所泄露的內(nèi)存蚤霞。
  • Abandon Memory 這種是循環(huán)引用挑豌,無法釋放掉的內(nèi)存安券。

2、循環(huán)引用

實質(zhì):多個對象相互之間有強引用浮毯,不能釋放讓系統(tǒng)回收完疫。

如何解決循環(huán)引用?

  • 1债蓝、避免產(chǎn)生循環(huán)引用壳鹤,通常是將strong引用改為 weak 引用。
  • 在合適時機去手動斷開循環(huán)引用饰迹。

MRC 下芳誓,__block不會增加其引用計數(shù)余舶,避免了循環(huán)引用
ARC 下,__block 修飾對象會被強引用锹淌,無法避免循環(huán)引用匿值,需要手動解除。

NSTimer 循環(huán)引用屬于相互循環(huán)使用
創(chuàng)建 NSTimer 作為其屬性赂摆,由于定時器創(chuàng)建后也會強引用該控制器對象挟憔,那么該對象和定時 器就相互循環(huán)引用了。
將定時器invalidate 并置為nil 即可

3烟号、懸垂指針绊谭?野指針?

懸垂指針:指針指向的內(nèi)存已經(jīng)被釋放了,但是指針還存在汪拥,這就是一個 懸垂指針 或者說 迷途指針

野指針: 沒有進行初始化的指針达传,其實都是 野指針

retain,copy,assign,weak,_Unsafe_Unretain

Strong 修飾符表示指向持有該對象,其修飾對象的引用計數(shù)會加 1迫筑。該對象只要引用計數(shù)不為 0 就不會 被銷毀宪赶。當然可以通過將變量強制賦值 nil 來進行銷毀。

weak 修飾符指向但是并不持有對象脯燃,引用計數(shù)也不會加 1搂妻。在 Runtime 中對該屬性進行了相關(guān)操作, 無需處理曲伊,可以自動銷毀叽讳。weak 用來修飾對象追他,多用于避免循環(huán)引用的地方坟募。weak 不可以修飾基本數(shù)據(jù) 類型。

assign主要用于修飾基本數(shù)據(jù)類型邑狸, 例如 NSInteger懈糯,CGFloat,存儲在棧中单雾,內(nèi)存不用程序員管理赚哗。assign 是可以修飾對象的,但是會出現(xiàn)問 題硅堆。

copy 關(guān)鍵字和 strong 類似屿储,copy 多用于修飾有可變類型的不可變對象 NSString,NSArray,NSDictionary 上。

__unsafe_unretain 類似于weak 渐逃,但是當對象被釋放后够掠,指針已然保存著之前的地址,被釋放后的地址 變?yōu)?僵尸對象茄菊,訪問被釋放的地址就會出問題疯潭,所以說他是不安全的赊堪。

__autoreleasing 將對象賦值給附有 __autoreleasing 修飾的變量等同于 ARC 無效時調(diào)用對象的 autorelease 方法,實質(zhì) 就是扔進了自動釋放池。

4竖哩、深拷貝 和 淺拷貝 集合類深拷貝如何實現(xiàn)

深拷貝: 該對象是否復(fù)制一份哭廉,內(nèi)容拷貝
淺拷貝: 指針拷貝,

對于集合對象的內(nèi)容復(fù)制僅僅是對對象本身相叁,但是對象的里面的元素還是指針復(fù)制遵绰。要想復(fù)制整個 集合對象,就要用集合深復(fù)制的方法增淹,有兩種:

(1)使用 initWithArray:copyItems:方法街立,將第二個參數(shù)設(shè)置為 YES 即可

NSDictionary * dct  = [[NSDictionary alloc] initWithDictionary:dicto copyItems:YES]

(2)將集合對象進行歸檔(archive)然后解歸檔(unarchive):

 NSArray *trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:oldArr]]

5、Dealloc 的實現(xiàn)機制

一埠通、Dealloc調(diào)用流程

  • 1.首先調(diào)用 _objc_rootDealloc()
  • 2.接下來調(diào)用 rootDealloc()
  • 3.這時候會判斷是否可以被釋放赎离,判斷的依據(jù)主要有 5 個,判斷是否有以上五種情況
  • NONPointer_ISA
  • weakly_reference
  • has_assoc
  • has_cxx_dtor
  • has_sidetable_rc
  • 4-1.如果有以上五中任意一種端辱,將會調(diào)用object_dispose()方法梁剔,做下一步的處理。
  • 4-2.如果沒有之前五種情況的任意一種舞蔽,則可以執(zhí)行釋放操作荣病,C 函數(shù)的 free()。
  • 5.執(zhí)行完畢渗柿。
- (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;
}

2.object_dispose() 調(diào)用流程个盆。

  • 1.直接調(diào)用 objc_destructInstance()
  • 2.之后調(diào)用 C 函數(shù)的 free()朵栖。
id 
object_dispose(id obj)
{
    if (!obj) return nil;

    objc_destructInstance(obj);    
    free(obj);

    return nil;
}

3.objc_destructInstance() 調(diào)用流程

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;
}

  • 1.先判斷 hasCxxDtor颊亮,如果有 C++ 的相關(guān)內(nèi)容,要調(diào)用 object_cxxDestruct() 陨溅,銷毀 C++ 相關(guān)的內(nèi)容终惑。
  • 2.再判斷 hasAssocitatedObjects,如果有的話门扇,要調(diào)用 object_remove_associations()雹有, 銷毀關(guān)聯(lián)對象的一系列操作。
  • 3.然后調(diào)用 clearDeallocating()臼寄。
  • 4.執(zhí)行完畢霸奕。

4.clearDeallocating() 調(diào)用流程。

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());
}
  • 1.先執(zhí)行 sideTable_clearDellocating()吉拳。
  • 2.再執(zhí)行 weak_clear_no_lock,在這一步驟中质帅,會將指向該對象的弱引用指針置為 nil。
  • 3.接下來執(zhí)行 table.refcnts.eraser(),從引用計數(shù)表中擦除該對象的引用計數(shù)临梗。
  • 4.至此為止涡扼,Dealloc 的執(zhí)行流程結(jié)束。

7 盟庞、內(nèi)存中的 5 大區(qū)

  • 棧區(qū)(stack):由編譯器自動分配釋放 吃沪,存放函數(shù)的參數(shù)值,局部變量的值等什猖。其 操作方式類似于 數(shù)據(jù)結(jié)構(gòu)中的棧票彪。
  • 堆區(qū)(heap):一般由程序員分配釋放, 若程序員不釋放不狮,程序結(jié)束時可能由 OS 回收 降铸。注意它與 數(shù)據(jù)結(jié)構(gòu)中的堆是兩回事,分配方式倒是類似于鏈表摇零。
  • 全局區(qū)(靜態(tài)區(qū))(static):全局變量和靜態(tài)變量的存儲是放在一塊的推掸,初始化的 全局變量和靜態(tài) 變量在一塊區(qū)域, 未初始化的全局變量和未初始化的靜態(tài)變量在相鄰的另一塊區(qū)域驻仅。 - 程序結(jié)束后 由系統(tǒng)釋放谅畅。
  • 文字常量區(qū):常量字符串就是放在這里的。 程序結(jié)束后由系統(tǒng)釋放噪服。
  • 程序代碼區(qū):存放函數(shù)體的二進制代碼毡泻。

8、內(nèi)存管理方案

  • taggedPointer :存儲小對象如 NSNumber粘优。深入理解 Tagged Pointer
  • NONPOINTER_ISA(非指針型的 isa):在 64 位架構(gòu)下仇味,isa 指針是占 64 比特位的,實際上只有 30 多位就 已經(jīng)夠用了雹顺,為了提高利用率丹墨,剩余的比特位存儲了內(nèi)存管理的相關(guān)數(shù)據(jù)內(nèi)容
  • 散列表第一位的 0 或 1 代表是純地址型 isa 指針,還是 NONPOINTER_ISA 指針无拗。
  • 第二位带到,代表是否有關(guān)聯(lián)對象
  • 第三位代表是否有 C++ 代碼昧碉。
  • 接下來 33 位代表指向的內(nèi)存地址
  • 接下來有 弱引用 的標記
  • 接下來有是否 delloc 的標記....等等
  • 復(fù)雜的數(shù)據(jù)結(jié)構(gòu)英染,包括了引用計數(shù)表和弱引用表 通過 SideTables()結(jié)構(gòu)來實現(xiàn)的,SideTables()結(jié)構(gòu)下被饿,有很多 SideTable 的數(shù)據(jù)結(jié)構(gòu)四康。 而 sideTable 當中包含了自旋鎖,引用計數(shù)表狭握,弱引用表闪金。 SideTables()實際上是一個哈希表,通過對象的地址來計算該對象的引用計數(shù)在哪個 sideTable 中
  • SideTables表在 非嵌入式的 64 位系統(tǒng)中,有 64 張 SideTable 表
  • 每一張 SideTable 主要是由三部分組成哎垦。自旋鎖囱嫩、引用計數(shù)表、弱引用表漏设。
  • 全局的 引用計數(shù) 之所以不存在同一張表中墨闲,是為了避免資源競爭,解決效率的問題郑口。
  • 引用計數(shù)表 中引入了 分離鎖的概念鸳碧,將一張表分拆成多個部分,對他們分別加鎖犬性,可以實現(xiàn)并發(fā)操 作瞻离,提升執(zhí)行效率

自旋鎖:

  • 自旋鎖是“忙等”的鎖。
  • 適用于輕量訪問乒裆。

引用計數(shù)表和弱引用表實際是一個哈希表套利,來提高查找效率。

9鹤耍、內(nèi)存布局

  • 棧(stack):方法調(diào)用日裙,局部變量等,是連續(xù)的惰蜜,高地址往低地址擴展
  • 堆(heap):通過 alloc 等分配的對象昂拂,是離散的,低地址往高地址擴展抛猖,需要我們手動控制
  • 未初始化數(shù)據(jù)(bss):未初始化的全局變量等
  • 已初始化數(shù)據(jù)(data):已初始化的全局變量等
  • 代碼段(text):程序代碼

10格侯、@dynamic

@dynamic 意味著編譯器不會幫助我們自動合成 settergetter 方法。我們需要手動實現(xiàn)财著、這里就涉及 到 Runtime 的動態(tài)添加方法的知識點联四。

11、@autoreleasePool 的數(shù)據(jù)結(jié)構(gòu)

  • 簡單說是雙向鏈表撑教,每張鏈表頭尾相接朝墩,有 parent、child 指針 ;
  • 每創(chuàng)建一個池子伟姐,會在首部創(chuàng)建一個 哨兵 對象,作為標記;
  • 最外層池子的頂端會有一個next 指針收苏。當鏈表容量滿了,就會在鏈表的頂端愤兵,并指向下一張表鹿霸。

objc_autoreleasePoolPush: 把當前 next 位置置為 nil,即哨兵對象,然后 next 指針指向下一個可入棧位置秆乳, AutoreleasePool 的多層嵌套懦鼠,即每次 objc_autoreleasePoolPush钻哩,實際上是不斷地向棧中插入哨兵 對象。
objc_autoreleasePoolPop: 根據(jù)傳入的哨兵對象找到對應(yīng)位置肛冶。 給上次 push 操作之后添加的對象依次發(fā)送release 消息街氢。 回退next指針到正確的位置。

12睦袖、弱引用管理

  • 添加 weak 變量:通過哈希算法位置查找添加阳仔。如果查找對應(yīng)位置中已經(jīng)有了當前對象所對應(yīng)的弱引用 數(shù)組,就把新的弱引用變量添加到數(shù)組當中扣泊;如果沒有近范,就創(chuàng)建一個弱引用數(shù)組,并將該弱引用變量 添加到該數(shù)組中延蟹。 ? 當一個被 weak 修飾的對象被釋放后评矩,weak 對象怎么處理的? 清除 weak 變量阱飘,同時設(shè)置指向為 nil斥杜。當對象被 dealloc 釋放后,在 dealloc 的內(nèi)部實現(xiàn)中沥匈,會調(diào)用弱 引用清除的相關(guān)函數(shù)蔗喂,會根據(jù)當前對象指針查找弱引用表,找到當前對象所對應(yīng)的弱引用數(shù)組高帖,將數(shù) 組中的所有弱引用指針都置為 nil缰儿。

__weak 屬性修飾的變量,如何實現(xiàn)在變量沒有強引用后自動置為 nil 散址?

用的弱引用 - weak表乖阵。也是一張 哈希表。
weak修飾的指針變量所指向的地址是 key 预麸,所有指向這塊內(nèi)存地址的指針會被添加在一個數(shù)組里瞪浸, 這個數(shù)組是 Value。當內(nèi)存地址銷毀吏祸,數(shù)組里的所有對象被置為 nil对蒲。
weak修飾的指針變量,在指向的內(nèi)存地址銷毀后贡翘,會在 Runtime 的機制下蹈矮,自動置為nil

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末床估,一起剝皮案震驚了整個濱河市含滴,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌丐巫,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異递胧,居然都是意外死亡碑韵,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進店門缎脾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來祝闻,“玉大人,你說我怎么就攤上這事遗菠×” “怎么了?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵辙纬,是天一觀的道長豁遭。 經(jīng)常有香客問我,道長贺拣,這世上最難降的妖魔是什么蓖谢? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮譬涡,結(jié)果婚禮上闪幽,老公的妹妹穿的比我還像新娘。我一直安慰自己涡匀,他們只是感情好盯腌,可當我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著陨瘩,像睡著了一般腊嗡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上拾酝,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天燕少,我揣著相機與錄音,去河邊找鬼蒿囤。 笑死客们,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的材诽。 我是一名探鬼主播底挫,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼脸侥!你這毒婦竟也來了建邓?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤睁枕,失蹤者是張志新(化名)和其女友劉穎官边,沒想到半個月后沸手,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡注簿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年契吉,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片诡渴。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡捐晶,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出妄辩,到底是詐尸還是另有隱情惑灵,我是刑警寧澤,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響敞贡,放射性物質(zhì)發(fā)生泄漏纵寝。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦把敢、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至桑阶,卻和暖如春柏副,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蚣录。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工割择, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人萎河。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓荔泳,卻偏偏與公主長得像,于是被迫代替她去往敵國和親虐杯。 傳聞我的和親對象是個殘疾皇子玛歌,可洞房花燭夜當晚...
    茶點故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內(nèi)容