iOS 多線程和內(nèi)存管理

內(nèi)存管理

  • iOS 管理對象 采用的是內(nèi)存引用計數(shù)管理 當(dāng)我們生產(chǎn)一個對象。

    • (生成對象) 我們就對這個對象引用計數(shù)加一 ,當(dāng)我們在對這個對象 去做某些事(持有對象) 相當(dāng)于我們持有這個對象 這事這個對象的引用計數(shù)又會 加一 。當(dāng)我們做完某些事 不需要這個對象的時候 (釋放對象)我們會對這個對象引用計數(shù)減一。當(dāng)我們完全不使用的時候 我們應(yīng)該對這個對象 進行銷毀(廢棄對象
    • 生成并持有對象 (alloc、new、copy、mutableCopy等方法
    • 持有對象 (retain 方法
    • 釋放對象(release 方法
    • 廢棄對象(dealloc方法
  • ARC 自動引用計數(shù)

    • 自動引用計數(shù) 是Xcode 4.2或以上版本 LLVM 編譯器 3.0 或以上版本引入新技術(shù)。
    • 在編寫代碼時 無需再次鍵入 retain 或 release 代碼 這在降低程序崩潰、內(nèi)存泄露等風(fēng)險的同時 很大程度上減少了開發(fā)程序的工作量。
  • GNU 和蘋果源碼 引用計數(shù)區(qū)別

    • GNU
      • alloc 類方法用 struct obj_layout中的retained 整數(shù)來保存引用計數(shù),并寫入對象內(nèi)存頭部(GNU 少量代碼可以完成、能夠統(tǒng)一管理引用計數(shù)用內(nèi)存塊與對象用內(nèi)存塊)
    • 蘋果
    • 蘋果實現(xiàn)大概使用了散列表來管理引用計數(shù)(對象內(nèi)存塊的分配無需考慮內(nèi)存塊頭部、引用計數(shù)表各記錄中存有內(nèi)存塊地址,可從各個記錄追溯到各對象的內(nèi)存塊
  • autorelease

    • NSAutoreleasePool 對象的生存周期相當(dāng)于C語言變量的作用域。對于所有autourelease 實例對象,在廢棄NSAutorelease 對象時,都將調(diào)用release 方法。
    • NSRunLoop 每次循環(huán)過程中NSAutoreleasePool 對象被生成或廢棄(NSAutoreleasePool 生命周期 在與NSRunLoop開發(fā)-結(jié)束)
    • GNU autorelease 源碼實現(xiàn)
      • autorelease 實例方法本質(zhì)調(diào)用 NSAutoreleasePool 對象的 addObject 方法
      • NSAutoreleasePool 內(nèi)部保存對象池子是一個數(shù)組。 經(jīng)常有人問 如果嵌套生成或持有 NSAutoreleasePool 對象 那個pool釋放會有效果 連接列表 所有的對象池子都是一個,這個相同于往 MutableAry 對象追加數(shù)據(jù)一樣
  • 修飾符

    • __strong==strong==copy 修飾符 ARC 下 通過 strong 修飾符 不必再次寫入 retain 或 release 代碼。通過 strong 修飾 自己生成的對象 自己持有 和 非自己生產(chǎn)的對象 自己也能持有。廢棄帶有strong 修飾的變量 或者對變量賦值 都可以做到不再需要自己持有的對象釋放。
    • __weak==weak 修飾符 weak 修飾符可以避免循環(huán)引用 弱引用不能持有對象實例,在超出變量作用域時 對象就會釋放 這樣就可以解決循環(huán)引用問題。
      • 用weak 修飾對象 若該對象被廢棄,則此弱引用將自動失效且處于nil 被賦值的狀態(tài)
      • 在weak 修飾的變量 在訪問引用對象 其實是訪問注冊到autoureleasePool的對象,這是因為在訪問引用對象過程中,該對象有可能被廢棄 把對象注冊到autoureleasePool 里面 在autoureleasePool 結(jié)束之前都能保證該對象存在。
        • 如果大量使用附有weak修飾的變量 注冊到autoureleasePool的對象也會大量的增加 因此在使用附有weak修飾的變量 最好暫時用strong 修飾符之后再使用 。用strong 修飾之后只會在autoureleasePool 注冊一次 大大減少了系統(tǒng)工作量
      • 當(dāng)用weak 修飾對象時 weak修飾的變量的地址注冊到weak表里(weak表與引用計數(shù)表相同都是散列表) 當(dāng)對象廢棄時 則把變量的地址從weak表中刪除。
        • 對象廢棄步驟
        • 1 、從weak表中獲取廢棄對象的地址為鍵值的記錄
        • 2、將包含在記錄中所有附有weak 修飾變量的地址 賦值為nil
        • 3、從weak 表中刪除該記錄
        • 4、從引用計數(shù)表中刪除廢棄對象的地址為鍵值的記錄
      • 如果大量的使用weak修飾符變量 則會消耗相應(yīng)的CPU 資源。(只需要避免循環(huán)引用時使用)
  • __unsafe_unretained==assign 修飾符 作用類似weak 修飾符 但在使用 __unsafe_unretained 要確保被賦值的對象卻是存在。 當(dāng)對象廢棄是 _unsafe_unretained修飾對象 不會置nil

Block

  • Block 簡介
    • Block 是帶有自動變量的匿名函數(shù)。 匿名函數(shù)就是不帶名稱函數(shù)
    • 表達式: ^ 返回值類型 參數(shù)列表 表達式
    • Block類型變量與C語言變量完全相同
      - 自動變量
      - 函數(shù)參數(shù)
      - 靜態(tài)變量
      - 靜態(tài)全局變量
      - 全部變量
    • Block 本質(zhì)
      • OC 中由類生成對象 意味著結(jié)構(gòu)體這樣 生成由改類生成的對象的結(jié)構(gòu)體實例。生成的各個對象 即由改類生成的對象各個結(jié)構(gòu)體實例 通過成本變量isa 保存該類的結(jié)構(gòu)體實例指針。通過clang 可以看到 Block 指針賦值給Block的結(jié)構(gòu)體成員變量isa 所以Block也是oc對象。
    • Block 變量截取
      • 截獲自動變量值 在執(zhí)行Block 語法時 Block 語法表達式所使用的自動變量值被保存到Block的結(jié)構(gòu)實例中(既Block 中),自動變量值被Block 截獲 只能執(zhí)行Block語法瞬間值。保存后就不能修改此值。
      • Block 中使用自動變量后 在Block 的結(jié)構(gòu)體實例中重寫改自動變量也不會改變原先截獲的自動變量
      • 靜態(tài)變量 、靜態(tài)全局變量、全局變量 可以在Block 中修改
        -Block截獲 靜態(tài)變量時 靜態(tài)變量是將指針 轉(zhuǎn)遞給 Block結(jié)構(gòu)體的構(gòu)造函數(shù)保存,這也是超出作用域使用變量的最簡單的方法
    • __block 說明符
      • __block 是存儲域說明符 用于指定將變量值設(shè)置到那個存儲域中。
      • 當(dāng)我們用 _block修飾變量 _block也同Block 一樣變成__Block_byref_val_0 結(jié)構(gòu)體類型的自動變量,即棧上生成的_Block_byref_val_0結(jié)構(gòu)體實例。這意味著該結(jié)構(gòu)體持有相當(dāng)于原自動變量的成員變量。
      • __Block_byref_val_0 結(jié)構(gòu)體實例的成員變量 _forwarding 持有指向該實例自身的指針。通過成員變量_forwarding訪問成員變量val (val 就是截獲的自動變量)
      • 為什么_forwarding會指向該實例自身的指針呢?
        • 配置全局Block 從變量作用域也可以通過指針安全地使用,但設(shè)置在棧上Block 如果所屬的變量作用域結(jié)束 該block就被廢棄。由于__block變量也配置在棧上,同樣的 如果所屬的變量作用域結(jié)束,則block變量也會被廢棄。
        • Blocks 提供了將Block 和 __block 變量從棧上賦值到堆上方法。將棧上Block 賦值到堆上 即使變量作用域結(jié)束 堆上的block 還可以繼續(xù)存在。(棧上_forwarding指向復(fù)制到堆上_block變量結(jié)構(gòu)體)
        • __block 變量用結(jié)構(gòu)體成員變量_forwarding可以實現(xiàn)無論_block 變量配置在棧上還是在堆上都能夠正確的訪問
    • Block存儲類型
      • 全局Block 在使用全局變量的地方不能使用自動變量 所以不存在對自動變量的截獲
      • 棧Block
      • 堆Block
    • 截獲對象
      • 當(dāng)Block 截獲oc 對象 會在_mian_blokc_desc_0 結(jié)構(gòu)體中增加的成員變量copy和dispose函數(shù) ,作為指針賦值給成員變量_main_block_copy_0函數(shù) 和_mian_blokc_dispose_0函數(shù)
        • _main_block_copy_0 里面調(diào)用 block_object_assgin 相當(dāng)于 retain 實例方法函數(shù),將對象賦值在對象類型的結(jié)構(gòu)成員變量中(調(diào)用時機 棧上block 復(fù)制到堆上)
        • _mian_blokc_dispose_0 里面調(diào)用 block_object_dispose 函數(shù) 相當(dāng)于release實例函數(shù)方法 釋放賦值在對象類型的結(jié)構(gòu)體成員變量中的對象(調(diào)用時機 堆上block 被廢棄時)
      • 什么時候棧上block 會被復(fù)制到堆上?
        • 調(diào)用 block 的copy 實例方法
        • block 作為函數(shù)的返回值返回時
        • 將block 賦值給附有 strong 修飾符 id類型或block 成員變量時
  • Block 循環(huán)引用
    • 可以通過weak 和 unsafe_unretained 來修飾 但要注意unsafe_unretained 修飾變量銷毀值 不置nil 情況
    • 也可以使用__block 來修飾 配合。 但要注意block 必須執(zhí)行block 要不然就會出現(xiàn)循環(huán)引用問題

GCD

  • Dispatch Queue
    • 執(zhí)行處理的等待隊列 。通過dispatch_async 函數(shù)等API,在block 語法中編寫想要執(zhí)行的處理并將其追加到 Dispatch Queue 中。 Dispatch Queue 按照追加的順序FIFO 執(zhí)行處理。
    • Serial Dispatch Queue (串行) 等待現(xiàn)在執(zhí)行中任務(wù)處理結(jié)束
    • Concurrent Dispatch Queue(并行)不等待現(xiàn)在執(zhí)行中任務(wù)處理結(jié)束
  • GCD API
    • dispatch_after

      • dispatch_after 函數(shù)并不是在指定時間后執(zhí)行處理,而是指定時間追加處理到Dispatch Queue。
      • 因為Mian Dispatch Queue 在主線程的RunLoop 中執(zhí)行,所以在比如每隔1/60 秒執(zhí)行RunLoop 中 Block 最快在3秒后執(zhí)行,最慢在3秒+1/60 秒執(zhí)行,并且 在Mian Dispatch Queue 有大量任務(wù)追加或主線程的處理本身有延遲的。
    • Dispatch Group

      • 當(dāng)我們處理多個并行隊列時想在多個任務(wù)處理結(jié)束時 執(zhí)行其他操作 。我們這是應(yīng)該就需要用 Dispatch Group,使用Dispatch Group 可以監(jiān)視任務(wù)結(jié)束狀態(tài),一旦監(jiān)視到任務(wù)結(jié)束,就可將結(jié)束的處理追加到Dispatch Queue 中。
      • dispatch_group_notify 函數(shù)會將執(zhí)行Block 追加到Dispatch Queue 中 ,第一個參數(shù)指定要監(jiān)視的Dispatch Group 第三個參數(shù)Block 追加到第二參數(shù)Dispatch Queue中。
    • dispatch_barrier_async

      • dispatch_barrier_async 函數(shù)會等待追加到并行隊列并行任務(wù)處理全部結(jié)束之后 在將指定處理追加到該并行隊列中。 然后在由dispatch_barrier_async 函數(shù)追加處理執(zhí)行完畢后 并行隊列在恢復(fù)之前操作 執(zhí)行追加到該隊列的任務(wù)。
    • dispatch_sync&&dispatch_async

      • dispatch_async 函數(shù) async 意味 非同步 就是將指定block 非同步地追加到指定 Dispatch Queue 中,dispatc_async 函數(shù)不做任何等待。
      • dispatch_sync 函數(shù) sync 意味 同步 就是將指定block 同步地追加到指定 Dispatch Queue 中,在追加block結(jié)束之前 dispatch_sync 函數(shù)會一直等待
    • dispatch_apply

      • dispatch_apply 函數(shù)是dispatch_sync 函數(shù) 和Dispatch Group 的管理API。該函數(shù)按指定的次數(shù)將指定block 追加到指定Dispatch Queue 中 并等待全部出了執(zhí)行結(jié)束。
    • Dispatch Semaphore

      • Dispatch Semaphore 是持有計數(shù)的信號,該計數(shù)底是多線程編程中的計數(shù)類型信號,計數(shù)為0時等待 計數(shù)為1或大于1時,減去1而不等待
      • dispatch_semaphore_create(X) X為計數(shù)初始值 保存可訪問對象線程個數(shù)
      • dispatch_semaphore_wait 是將 Dispatch Semaphore 的計數(shù) 值減一 (由于 Dispatch Semaphore 的計數(shù)值大于等于1 所以 Dispatch Semaphore 的計數(shù) 值減一)
      • dispatch_semaphore_signal 是將 Dispatch Semaphore 的計數(shù) 值加一 (排他控制處理結(jié)束 將 Dispatch Semaphore 的計數(shù) 值加一 恢復(fù)之前狀態(tài))
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。