Block深刻淺出

    • 研究工具
      • clang 爲了研究編譯器的實現原理,咱們須要使用 clang 命令。clang 命令能夠將 Objetive-C 的源碼改寫成 C / C++ 語言的,藉此能夠研究 block 中各個特性的源碼實現方式。
      • clang -rewrite-objc main.m
      • main.m中不能包含UIKit框架,命令行中解析沒法識別。包含#import <Foundation/Foundation.h>是能夠支持的
    • C語言中變量有哪幾種
      • 自動變量
      • 函數參數 
      • 靜態變量
      • 靜態全局變量
      • 全局變量
    • 每種變量類型在Block中的特性及原理
      • 自動變量
        • 不能夠修改,攜帶__block修飾能夠被修改
        • 會被Block持有(retainCount+1)
        • 不帶__block修飾的會被copy進Block
      • 函數參數 
        • 能夠直接修改
        • 不會被Block持有(retainCount不會增長)
      • 靜態變量
        • 能夠被修改 - 因爲傳遞給Block是內存地址值,查看Block的具體實現(查看clang後的main.cpp文件)
      • 靜態全局變量和全局變量
        • 能夠直接被訪問和修改 - 因爲存儲區域在區全局區,因爲做用區域的緣由
        • 不會被Block持有(retainCount不會增長)
    • Block中改變變量值的方式
      • 傳遞內存地址到Block
        • 指針所指向的內存不可修改,可是內存中存放的數據能夠修改
        • NSMutableString 變量能夠直接在Block體中被appendString,可是不能夠被=
      • 使用__block修飾
        • Block會將此標識符修飾的變量轉化成一個結構體,Block體中傳遞而且使用的是這個結構體
        • __block int i 會被轉換成
        • struct __Block_byref_i_0 {
            void *__isa; //指向本身
          __Block_byref_i_0 *__forwarding; //指向本身,當被copy到堆(heap)上時,原Block此字段指向堆上的Block地址,對上的此字段仍然指向本身。這樣無論__block怎麼複製到堆上,仍是在棧上,均可以經過(i->__forwarding->i)來訪問到變量值。
           int __flags;
           int __size;
           int i;
          };
           
      • Block捕獲外部變量僅僅只捕獲Block閉包裏面會用到的值,其餘用不到的值,它並不會去捕獲。並且Block能捕獲的變量只有自動變量和靜態變量了。
    • Block的種類
      • _NSConcreteStackBlock
        • 只用到外部局部變量、成員屬性變量,且沒有強指針引用的block都是StackBlock
        • StackBlock的生命週期由系統控制的,一旦返回以後,就被系統銷燬了
        • 不持有對象
        • 對Block的retain,release造做無效,copy造做會變成_NSConcreteMallocBlock類型
      • _NSConcreteMallocBlock
        • 有強指針引用或copy修飾的成員屬性引用的block會被複制一份到堆中成爲MallocBlock
        • 沒有強指針引用即銷燬,生命週期由程序員控制
        • 持有對象
        • retain,release,copy操做生效,內存管理器中的計數會增長。(但retainCount始終爲1)
      • _NSConcreteGlobalBlock
        • 沒有用到外界變量或只用到全局變量、靜態變量的block爲_NSConcreteGlobalBlock
        • 生命週期從建立到應用程序結束
        • 不持有對象
        • retain,release,copy操做爲空操做
      • ARC下,系統會根據下面的規則決定是否將Block複製到heap上
    • 系統調用copy對Block複製的狀況
      • 手動調用copy(當Block爲函數參數的時候,就須要咱們手動的copy一份到堆上了。這裏除去系統的API咱們不須要管,好比GCD等方法中自己帶usingBlock的方法)
      • Block是函數的返回值
      • Block被強引用(Block被賦值給__strong或者id類型)
      • 調用系統API入參中含有usingBlcok的方法
    • __block堆棧拷貝
      • MRC 只有發生了copy,__block修飾的對象纔會被copy到堆上
      • ARC 發生了copy或者=(block 類型經過=進行傳遞時,會致使調用objc_retainBlock->_Block_copy->_Block_copy_internal方法鏈),__block修飾的對象纔會被copy到堆上
      • __block修飾的對象纔會被copy到堆上 : __NSStackBlock__ 類型的 block 轉換爲 __NSMallocBlock__ 類型
    • clang代碼轉換
      • main.m 文件30行,大小831字節。轉換後main.cpp 文件104810行,大小3.1MB。
    • Block 循環引用
      • 引發循環引用的條件其實很苛刻:
        • Block須要被相關類(當前類或者嵌套引用的某各種)retain或copy等相似操做
        • Block體中使用self(包括成員變量,成員屬性等)
      • 發生循環引用的拆解方式:
        • 使用__weak對self進行弱引用,實際上是經過弱引用的方式將閉環解開
          • __weak __typeof(self) wself = self;
            self.myBlock = ^{
                __strong __typeof(wself) self = wself;
                // 使用self進行相關操做便可
            };
        • 使用形參的方式,將self做爲參數傳遞給Block
      • 常見易混淆的場景(前提:Block沒有被retain或copy的狀況下,即苛刻條件中的第一條)
        • GCD,系統動畫等系統Block API,Block體中直接使用self不會有問題
        • Block體中使用了成員屬性或者成員變量,不會有問題 (參考Block種類)
        • 訪問了靜態變量,全局變量,全局靜態變量,不會引發問題
相關文章
相關標籤/搜索