block
底層就是一個struct __main_block_impl_0
類型的結構體
,這個結構體中包含一個isa
指針,本質上是一個OC
對象block
是封裝了函數調用
以及函數調用環境
的OC
對象block底層結構 block
底層結構就是__main_block_impl_0
結構體,內部包含了impl結構體
和Desc結構體
以及外部須要訪問的變量
,block
將須要執行的代碼放到一個函數裏,impl
內部的FuncPtr
指向這個函數的地址,經過地址調用這個函數,就能夠執行block
裏面的代碼了。Desc
用來描述block
,內部的reserved
做保留,Block_size
描述block
佔用內存 git
block的變量捕獲 局部變量block
訪問方式是值傳遞
,auto自動變量
可能會銷燬,內存可能會消失,不採用指針訪問; 局部靜態變量block
訪問方式是指針傳遞
,static變量
一直保存在內存中,指針訪問便可; 全局變量、靜態全局變量block
不須要對變量捕獲,直接取值 github
// block的變量捕獲代碼解析以下
auto int age = 10;
static int height = 10;
void (^block)(void) = ^{
NSLog(@"age is %d,height is %d",age,height);
};
age = 20;
height = 20;
block();
-------------------------------------------------
output: age is 10,height is 20
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int age; // 值傳遞
int *height; // 指針傳遞
}
複製代碼
block類型 | 環境 | 存儲域 | copy操做後 |
---|---|---|---|
__NSGlobalBlock__ |
沒有訪問auto變量 |
數據區 | 什麼也不作,類型不改變 |
__NSStackBlock__ |
訪問了auto變量 |
棧區 | 從棧複製到堆,類型改變爲__NSMallocBlock__ |
__NSMallocBlock__ |
__NSStackBlock__ 調用了copy |
堆區 | 引用計數+1 ,類型不改變 |
ARC
環境下,編譯器會根據如下幾種狀況自動將棧上的block
複製到堆上
: 一、block
做爲函數返回值時,好比使用=
二、將block
賦值給__strong
指針時 三、block
做爲Cocoa API
中方法名含有usingBlock
的方法參數時 四、block
做爲GCD API
的方法參數時block
內部訪問了對象類型的auto變量
時: 若是block在棧空間
,不管是ARC仍是MRC
環境,無論外部變量
是強引用仍是弱引用
,block
都會弱引用
訪問對象 若是block在堆空間
,若是外部強引用
,block
內部也是強引用
;若是外部弱引用
,block
內部也是弱引用
block
是在棧上
,將不會對auto變量
產生強引用
b) 棧上的block
隨時會被銷燬,也不必去強引用其餘對象block
內部的copy
函數 b) copy
函數內部會調用_Block_object_assign
函數 c) _Block_object_assign
函數會根據auto變量
的修飾符__strong
、__weak
、__unsafe_unretained
作出相應的操做,造成強引用
或者弱引用
二、若是block從堆上移除 a) 會調用block
內部的dispose
函數 b) dispose
函數內部會調用_Block_object_dispose
函數 c) _Block_object_dispose
函數會自動釋放引用的auto變量
(release
,引用計數-1
,若爲0
,則銷燬)__block
修飾符做用: __block
能夠用於解決block
內部沒法修改auto變量值
的問題 __block
不能修飾全局變量、靜態變量static
__block
修飾符原理: 編譯器會將__block
變量包裝成一個結構體__Block_byref_age_0
,結構體內部*__forwarding
是指向自身的指針,內部還存儲着外部auto變量
的值 __block
的forwarding
指針以下圖:
棧上
,__block
結構體中的__forwarding
指針指向本身
,一旦複製到堆上
,棧上的__block
結構體中的__forwarding
指針會指向堆上的__block
結構體,堆上__block
結構體中的__forwarding
仍是指向本身
。假設age
是棧上
的變量,age->__forwarding
會拿到堆上的__block
結構體,age->__forwarding->age
會把20
賦值到堆上
,不管是棧上仍是堆上的__block
結構體,都能保證20
賦值到堆的結構體
裏bash
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSMutableArray *array = [NSMutableArray array];
void (^block)(void) = ^{
[array addObject: @"5"];
[array addObject: @"5"];
NSLog(@"%@",array);
};
block();
}
return 0;
}
複製代碼
分析:能夠正確執行,由於在block
塊中僅僅是使用了array的內存地址
,往內存地址
中添加內容
,並無修改arry的內存地址
,所以array
不須要使用__block修飾
也能夠正確編譯。當僅僅是使用局部變量的內存地址
,而不是修改
的時候,儘可能不要添加__block
,經過上述分析咱們知道一旦添加了__block
修飾符,系統會自動建立相應的結構體,佔用沒必要要的內存空間函數
附:個人博客地址ui