OC底層-Block本質(2、變量捕獲)

前言

爲了保證block內部可以正常訪問外部的變量,block有一個變量捕獲機制。bash

局部變量

auto 變量(默認的auto)

在上一篇代碼中咱們已經瞭解過block對age變量的捕獲。 auto自動變量,離開做用域就銷燬,局部變量前面自動添加auto關鍵字。自動變量會捕獲到block內部,也就是說block內部會專門新增長一個參數來存儲變量的值。 auto只存在於局部變量中,訪問方式爲值傳遞,經過上述對age參數的解釋咱們也能夠肯定確實是值傳遞。函數

static變量

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

複製代碼
相關文章
相關標籤/搜索