Block的型別

Ly夢k發表於2019-02-23

前言

Block在iOS日常開發中極其常見,大家應該幾乎都使用過,比較熟悉它的用法,而且知道Block可能引起迴圈引用,今天來聊聊Block,以及Block造成記憶體洩露的根本原因。

Block是什麼

首先,Block和普通例項一樣是是一個物件,他有自己的isa指標。
它就是一個裡面儲存了指向定義程式碼塊的函式指標和block外部上下文變數資訊的結構體。通過斷點我們看到block的isa指標,如下圖:

isa

我們發現block的型別其實是不同的,這是為什麼接下來我們看看Block到底有哪些型別。

Block的型別

我們通過實際例子看看的各種型別的block

  • NSMallocBlock
- (void)NSMallocBlock {
    int tempInt = 1;
    void (^block)(void) = ^ {
        NSLog(@"----------%d----------\n\n",tempInt);
    };
    block();
    [self printBlockSuperClass:block];
}
複製程式碼

結果:NSMallocBlock -> __NSMallocBlock -> NSBlock -> NSObject

  • NSStaticBlock
- (void)NSStaticBlock {
    int tempInt = 1;
    __weak void (^block)(void) = ^ {
        NSLog(@"----------%d----------\n\n",tempInt);
    };
    block();
    
    [self printBlockSuperClass:block];
}
複製程式碼

結果:NSStackBlock -> __NSStackBlock -> NSBlock -> NSObject

  • NSGlobalBlock
- (void)NSGlobalBlock {
    void (^block)(int a) = ^ (int a){
        NSLog(@"----------%d----------\n\n",a);
    };
    block(1);
    
    [self printBlockSuperClass:block];
}
複製程式碼

結果:NSGlobalBlock -> __NSGlobalBlock -> NSBlock -> NSObject

我們發現:

  1. 當沒有外部變數時,block為__NSMallocBlock,它由開發者建立,儲存在堆記憶體上。
  2. 當有__weak修飾時block為__NSStackBlock,儲存在棧區。
  3. 當block有引數時(捕獲了外部變數時)block為__NSGlobalBlock,儲存在全域性區。

屬性關鍵字和外部變數型別對Block記憶體的影響

為了驗證我們定義了三中關鍵字的block,分別有storng、weak、copy修飾:

@property (nonatomic, strong) TestBlock strongBlock;
@property (nonatomic, weak) TestBlock weakBlock;
@property (nonatomic, copy) TestBlock copyBlock;
複製程式碼

驗證方法如下:

int globalInt = 1000;//全域性變數
static staticInt = 10000;//全域性靜態變數

- (void)blockInMemory {
    static tempStaticInt = 100000;//區域性靜態變數
    int normalInt = 20000;
    _strongBlock = ^(int tempInt) {
        NSLog(@"tempInt = %d", normalInt);
    };
    _weakBlock = ^(int tempInt) {
        NSLog(@"tempInt = %d", normalInt);
    };
    _copyBlock = ^(int tempInt) {
        NSLog(@"tempInt = %d", normalInt);
    };
    NSLog(@"\nstrongBlock:%@\n_weakBlock:%@\n_copyBlock:%@",object_getClass(_strongBlock),object_getClass(_weakBlock),object_getClass(_copyBlock));
}
複製程式碼

分別列印不同變數型別(全域性變數、全域性靜態變數、區域性靜態變數、區域性變數)和屬性關鍵字下block的型別,我們可以得出如下結論:

  1. 沒有外部變數時,三種Block都是 __NSGlobalBlock__
  2. 有外部變數時,
    2.1 外部變數時全域性變數、全域性靜態變數、區域性靜態變數時:__NSGlobalBlock__(全域性區)
    2.2 外部變數時普通外部變數:copy和strong修飾的Block是__NSMallocBlock__(堆區);weak修飾的block是__NSStackBlock__(棧區)

有普通外部變數的block是在棧區建立的,當有copy和strong修飾符修飾的時,會把block從棧移到堆區。

ARC下使用copy和strong關鍵字修飾block是一樣的。

結語

本篇為Block系列的第一篇,由此,我們瞭解了三種不同型別Block,接下來會以原始碼的方式深入瞭解block的底層實現,我們下篇再見。
Demo工程

相關文章