Block的記憶體結構
Block在iOS日常開發中極其常見,大家應該幾乎都使用過,比較熟悉它的用法,而且知道Block可能引起迴圈引用,今天來聊聊Block,以及Block造成記憶體洩露的根本原因。
Block是什麼
首先,Block和普通例項一樣是是一個物件,他有自己的isa指標。
它就是一個裡面儲存了指向定義程式碼塊的函式指標和block外部上下文變數資訊的結構體。通過斷點我們看到block的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
我們發現:
__weak
屬性關鍵字和外部變數型別對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的型別,我們可以得出如下結論:
- 沒有外部變數時,三種Block都是
__NSGlobalBlock__
- 有外部變數時,
2.1 外部變數時全域性變數、全域性靜態變數、區域性靜態變數時:__NSGlobalBlock__
(全域性區)
2.2 外部變數時普通外部變數:copy和strong修飾的Block是__NSMallocBlock__
(堆區);weak修飾的block是__NSStackBlock__
(棧區)