內(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)存塊)
- GNU
-
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 成員變量時
- 當(dāng)Block 截獲oc 對象 會在_mian_blokc_desc_0 結(jié)構(gòu)體中增加的成員變量copy和dispose函數(shù) ,作為指針賦值給成員變量_main_block_copy_0函數(shù) 和_mian_blokc_dispose_0函數(shù)
-
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))
-