1. 程式人生 > 其它 >iOS-block三種類型詳解

iOS-block三種類型詳解

Block有三種類型:__NSGlobalBlock,__NSStackBlock,__NSMallocBlock

問題:
Block有幾種型別呢?這幾種型別分別在什麼情況下出現?
我們思考一下,__NSStackBlock在訪問外部變數時,會有什麼問題?

我們在講block的本質的時候已經知道了,block的本質就是一個 OC 物件,那麼既然它是一個 OC 物件,它就會有型別,本文就將講解block的三種類型.並都繼承於NSBlock
我們在講block的三種類型之前,先了解一下程式的記憶體分配情況,因為不同型別的block分配的記憶體也不同.

  • .text段 : 也稱程式碼段,我們寫的程式碼都存放在這裡
  • .data區 : 也稱資料區,一般存放全域性變數, __NSGlobalBlock存放在這裡
  • 堆區 : 存放我們自己alloc出來的物件,動態分配記憶體,需要程式設計師自己申請記憶體,自己管理. __NSMallocBlock存放在堆區
  • 棧區 : 一般存放區域性變數,不需要程式設計師管理,系統自動分配,自動銷燬,__NSStackBlock存放在棧區
    不同block型別的記憶體分配

一: __NSGlobalBlock
結論: 沒有訪問 auto變數 的block 就是 __NSGlobalBlock

int main(int argc, const char
* argv[]) { @autoreleasepool { static int age = 10; void(^block)(void) = ^{ NSLog(@"Hello, World! %d",age); }; NSLog(@"%@",[block class]); } return 0; } 控制檯輸出:__NSGlobalBlock__

這個很好理解,不過多解釋

二: __NSStackBlock
結論:訪問了auto變數 的block 就是 __NSStackBlock

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int age = 10;
        void(^block)(void) = ^{
            NSLog(@"Hello, World! %d",age);
        };
        NSLog(@"%@",[block class]);
    }
    return 0;
}
控制檯輸出:__NSMallocBlock__

怎麼列印的是__NSMallocBlock__,剛才不是說訪問了auto變數就是__NSStackBlock嗎?
因為這裡我們使用的是ARC,在ARC環境下,Xcode編譯器再某些情況會預設幫我們做呼叫copy 變成堆block ,我們在Build Settings中把ARC設定成MRC,再來列印一下:

2018-08-30 17:37:09.846365+0800 block的型別[4318:3463149] __NSStackBlock__

這次列印的就是__NSStackBlock__

我們思考一下,__NSStackBlock在訪問外部變數時,會有什麼問題?

會出現野指標crash 所以在ARC壞境Xcode幫我們處理成了堆block(__NSMallocBlock__)防止出現釋放了還去訪問導致野指標crash

所以,為了避免出現這種情況,我們需要把block儲存在堆上,__NSMallocBlock就閃亮登場了.

三: __NSMallocBlock
結論: 當一個__NSStackBlock呼叫了copy操作,返回的就是一個__NSMallocBlock

思考:如果我們對__NSStackBlock 進行一次 copy操作,會發生什麼變化呢?

__NSStackBlock copy後

‼️注意

以上都是在MRC環境下
如果是在ARC環境下,編譯器會根據情況自動將棧上的block複製到堆上, 比如以下幾種情況:

  • 1. block作為函式返回值時

    block作為返回值編譯器會自動copy
  • 2.將block賦值給__strong指標時

    被強指標引用的block會自動copy
  • 3.block作為Cocoa API方法名含有UsingBlock的方法引數時

    UsingBlock
  • 4.block作為GCD API的方法引數時
GCD API的方法引數
  • 5.block呼叫copy方法
三種類型block的記憶體儲存以及每一種型別的block呼叫copy後的結果如下所示:

總結:

  • 1:一共有三種類型的Block.分為__NSGlobalBlock,__NSStackBlock,__NSMallocBlock.
    1. 沒有訪問 auto變數 的block就是__NSGlobalBlock
    2. 訪問了auto變數 的block就是__NSStackBlock
    3. 當一個__NSStackBlock呼叫了copy操作,返回的就是一個__NSMallocBlocksing
  • 2:在ARC環境下,編譯器會自動把棧上的block copy到堆上