這篇博客咱們從一個很常見的題目入手。c++
int age = 10; void (^myblock)(void) = ^{ NSLog(@"%d",age); }; age = 20; myblock();
這個題目就涉及到了block內訪問外部變量,block有個變量捕獲機制,數組
咱們新建一個mac的命令行工程,把上面代碼寫進去,而後用clang把main.m文件編譯爲cpp的文件看一下。xcode
具體的block底層結構上一篇文章咱們已經說過了,這裏咱們針對結構就不在贅述,直接說核心點。函數
struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int age; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int flags=0) : age(_age) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } };
能夠看到,在block的內部,多了一個age變量。並且從初始化的函數來看,這個__main_block_impl_0
中age的值是外面傳進來賦給他的。咱們看一下main函數中。命令行
int age = 10; void (*myblock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age)); age = 20; ((void (*)(__block_impl *))((__block_impl *)myblock)->FuncPtr)((__block_impl *)myblock);
能夠看出來在這個block初始化的過程當中,就把age的值,也就是10,傳了進去,賦值給了block內部的age變量。指針
那我再看一下打印的時候的age是什麼狀況。code
static void __main_block_func_0(struct __main_block_impl_0 *__cself) { int age = __cself->age; // bound by copy NSLog((NSString *)&__NSConstantStringImpl__var_folders_6l_80sfw0tn35bg4dxc51jlq_bw0000gn_T_main_3b5bf4_mi_0,age); }
打印的這個age就是block本身內部的這個ege變量(值爲10),因此咱們在執行block以前,改變外面的age值爲20,其實改變的不是內部要打印的這個age了,因此打印出來結果仍是10。對象
好的,咱們定義的這個age就是一個普通的局部變量,其實就是auto類型的局部變量(C語言基礎)。那咱們下面改變一下這個變量的屬性,嘗試一下靜態變量(static)和全局變量。內存
首先是靜態變量。博客
//定義變量時加static標識 static int age = 10;
運行結果是20。咱們依然要看一下clang編譯一下,看c++代碼。
struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int *age; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_age, int flags=0) : age(_age) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } };
咱們來分析一下兩次的異同,首先block都對變量進行了捕獲,但不一樣的是static類型的變量是捕獲了變量的指針,那咱們應該就能夠理解了,通常狀況下這種基本數據類型若是是傳指針,就意味要修改值的。
咱們從main函數中也能夠看出來,在初始化block的時候傳入了外部age的指針,
static int age = 10; void (*myblock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &age));
並且在打印的時候也是打印了這個指針指向的數據。
static void __main_block_func_0(struct __main_block_impl_0 *__cself) { int *age = __cself->age; // bound by copy NSLog((NSString *)&__NSConstantStringImpl__var_folders_6l_80sfw0tn35bg4dxc51jlq_bw0000gn_T_main_d618bf_mi_0,(*age)); }
因此咱們在執行block以前更改了age的值,在執行block的時候打印的也是同一塊內存的值,因此值改變了。
最後咱們在試一下全局變量。
在定義age的時候,定義成一個全局變量。(拿到main方法外面)。
int age = 10; struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } };
從編譯後的代碼能夠看出,在底層其實也是生成了一個age=10的全局變量,而後在block內部並無捕獲這個變量。
在無論是從新賦值,仍是輸出打印,都是操做的這個全局的age,因此這個值也是能被改變的。
可能有人會問若是是
static int age = 10;
這個樣子的全局變量呢?
全局變量加不加static都是同樣的。這裏就不上代碼細說了。
小結
局部變量:
全局變量:
上面咱們使用的是基本數據類型,那對象是否是也同樣呢?
其實對象類型的也是同樣的,可是有一個小點咱們須要瞭解一下。
看下面這個例子吧。
NSMutableArray * arr = [NSMutableArray new]; void (^myblcok)(void) = ^{ [arr addObject:@"1"]; NSLog(@"%@",arr); }; myblcok();
按照咱們上面總結的這種auto類型的局部變量是要被捕獲到內部的,可是應該不能夠修改值。
上面代碼執行後打印數組,是有@「1」這個元素的,這裏咱們就要搞清楚,咱們調用[arr addObject:@"1"];
並無修改arr的值,只是只用了這個指針。
若是咱們在block的回調中讓arr=nil;
,這算是改變arr的值,可是xcode就會報錯的了。