爲了保證block內部可以正常訪問外部的變量,block有一個變量捕獲機制。bash
在上一篇代碼中咱們已經瞭解過block對age變量的捕獲。 auto自動變量,離開做用域就銷燬,局部變量前面自動添加auto關鍵字。自動變量會捕獲到block內部,也就是說block內部會專門新增長一個參數來存儲變量的值。 auto只存在於局部變量中,訪問方式爲值傳遞,經過上述對age參數的解釋咱們也能夠肯定確實是值傳遞。函數
static 修飾的變量爲指針傳遞,一樣會被block捕獲。ui
接下來分別添加auto修飾的局部變量和static修飾的局部變量,重看源碼來看一下他們之間的差異:spa
auto int a = 10;
static int b = 10;
void(^block)(void) = ^{
NSLog(@"hello, a = %d, b = %d", a,b);
///打印 hello, a = 10, b = 20
};
a = 20;
b = 20;
block();
複製代碼
經過命令行咱們查看源代碼:命令行
從上述源碼中能夠看出,a,b兩個變量都有捕獲到block內部。可是a傳入的是值,而b傳入的則是地址。3d
爲何兩種變量會有這種差別呢,由於自動變量可能會銷燬,block在執行的時候有可能自動變量已經被銷燬了,那麼此時若是再去訪問被銷燬的地址確定會發生壞內存訪問,所以對於自動變量必定是值傳遞而不多是指針傳遞了。而靜態變量不會被銷燬,因此徹底能夠傳遞地址。而由於傳遞的是值得地址,因此在block調用以前修改地址中保存的值,block中的地址是不會變得。因此值會隨之改變。指針
咱們以一樣的方式來研究下block是否捕獲全局變量code
int a = 10;
static int b = 10;
int main(int argc, const char * argv[]) {
@autoreleasepool {
void(^block)(void) = ^{
NSLog(@"hello, a = %d, b = %d", a,b);
/// 打印hello, a = 20, b = 20
};
a = 20;
b = 20;
block();
}
return 0;
}
複製代碼
源代碼:cdn
經過上述代碼能夠發現,__main_block_imp_0並無添加任何變量,所以block不須要捕獲全局變量,由於全局變量不管在哪裏均可以訪問。 局部變量由於跨函數訪問因此須要捕獲,全局變量在哪裏均可以訪問 ,因此不用捕獲。 因此用下面這張圖作個總結:blog
總結:局部變量都會被block捕獲,自動變量是值捕獲,靜態變量爲地址捕獲。全局變量則不會被block捕獲
疑問思考:如下代碼中block是否會捕獲變量呢?
#import "Person.h"
@implementation Person
- (void)test
{
void(^block)(void) = ^{
NSLog(@"%@",self);
};
block();
}
- (instancetype)initWithName:(NSString *)name
{
if (self = [super init]) {
self.name = name;
}
return self;
}
@end
複製代碼