OC-Block的本質(zhì)(一)-底層結(jié)構(gòu)、變量捕獲

image-20210422152708323

本篇學(xué)習(xí)內(nèi)容

image-20210422152804224
image-20210422152831028
image-20210422152856465

block的本質(zhì)

block的原理是怎樣的?本質(zhì)是什么?

  • block本質(zhì)上也是一個OC對象,因為它的內(nèi)部也有個isa指針
  • block是封裝了函數(shù)調(diào)用以及函數(shù)調(diào)用環(huán)境的OC對象

通俗的理解:block就是將一些代碼封裝起來,以便在將來某個時候被使用,如果你不去調(diào)用block,block內(nèi)部封裝的代碼就不會執(zhí)行。

舉一個簡單的例子

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        ^{
            NSLog(@"this is a block!");
            NSLog(@"this is a block!");
            NSLog(@"this is a block!");
            NSLog(@"this is a block!");
        };
    }
    return 0;
}

RUN>

沒有任何輸出,Block代碼塊沒調(diào)用

Block的使用也很簡單,可以像函數(shù)一樣被使用。加上()就代表調(diào)用,如下

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        ^{
            NSLog(@"this is a block!");
            NSLog(@"this is a block!");
            NSLog(@"this is a block!");
            NSLog(@"this is a block!");
        }();
    }
    return 0;
}

RUN>

2021-04-22 15:35:54.161606+0800 Interview03-block[4322:148955] this is a block!
2021-04-22 15:35:54.162084+0800 Interview03-block[4322:148955] this is a block!
2021-04-22 15:35:54.162138+0800 Interview03-block[4322:148955] this is a block!
2021-04-22 15:35:54.162160+0800 Interview03-block[4322:148955] this is a block!

block的底層結(jié)構(gòu)-block的本質(zhì)探索

寫個簡單的block,其中block內(nèi)部使用了block外部的age變量:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
      
        int age = 20;

        void (^block)(int, int) =  ^(int a , int b){
            NSLog(@"this is a block! -- %d", age);
        };
        block(10, 10);
    }
    return 0;
}

RUN>

2021-04-22 15:38:10.449976+0800 Interview03-block[4340:150550] this is a block! -- 20

上面的代碼可以看出,block里面使用了它上面的 int age = 20 ,可以將這個先簡單的理解成函數(shù)調(diào)用環(huán)境,顧名思義,就是block所用到的一些外部變量。

通過clang編譯器執(zhí)行編譯成C++代碼:

$ xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m

clang編譯器編譯完后會得到一個.cpp格式的文件,這就是我們剛才轉(zhuǎn)換的.m文件的底層代碼.

main函數(shù)的C++代碼如下

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 

        int age = 20;
        //block底層定義
        void (*block)(int, int) = ((void (*)(int, int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age));
        //block底層調(diào)用
        ((void (*)(__block_impl *, int, int))((__block_impl *)block)->FuncPtr)((__block_impl *)block, 10, 10);
    }
    return 0;
}

但是由于底層的代碼添加了許多強轉(zhuǎn),我們簡化代碼,如下:

//block底層定義
void (*block)(void) = &__main_block_impl_0(
                                           __main_block_func_0,
                                           &__main_block_desc_0_DATA
                                           );
//block底層調(diào)用
block->FuncPtr(block, 10, 10);
block底層定義
//block底層定義
void (*block)(void) = &__main_block_impl_0(
                                           __main_block_func_0,
                                           &__main_block_desc_0_DATA
                                           );

先看__main_block_impl_0這個函數(shù),我們發(fā)現(xiàn)它被定義在一個同名結(jié)構(gòu)體里面,這個__main_block_impl_0結(jié)構(gòu)體就是block的底層實現(xiàn)

// 一: block底層數(shù)據(jù)結(jié)構(gòu)
struct __main_block_impl_0 {
    struct __block_impl impl; // 1: impl 結(jié)構(gòu)體
    struct __main_block_desc_0* Desc; // 2: block描述信息的結(jié)構(gòu)體
    int age; //3:捕獲的外部變量
    //4: 和結(jié)構(gòu)體同名的構(gòu)造函數(shù) ( C++語法 , 類似于 OC 的init方法,返回一個結(jié)構(gòu)體對象,類似于返回self)
    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int flags=0) : age(_age){
    impl.isa = &_NSConcreteStackBlock;//isa指向類對象,就比如str的isa指向NSString
    impl.Flags = flags;
    impl.FuncPtr = fp;//外面的__main_block_func_0函數(shù)地址傳進(jìn)來,保存在這里
    Desc = desc;//外面的__main_block_desc_0結(jié)構(gòu)體地址傳進(jìn)來,保存在這里
  }
};

通過底層代碼我們可以看到,block在底層中的數(shù)據(jù)結(jié)構(gòu)是一個結(jié)構(gòu)體,這個結(jié)構(gòu)體有四個部分組成:

1: struct __block_impl

2: struct __main_block_desc_0

3: 捕獲的外部變量

4:和block結(jié)構(gòu)體同名的構(gòu)造函數(shù)

我們找到struct __block_impl 結(jié)構(gòu)體:

//struct __block_impl 結(jié)構(gòu)體
struct __block_impl {
  void *isa; //指向 block 的類型
  int Flags;//按位表示block的附加信息
  int Reserved;//保留變量
  void *FuncPtr; //封裝了執(zhí)行 block 代碼塊的函數(shù)地址
};

發(fā)現(xiàn)這個結(jié)構(gòu)體里面第一個成員就是isa,驗證了block本質(zhì)上也是一個OC對象。

然后我們再找到struct __main_block_desc_0 結(jié)構(gòu)體 :

static struct __main_block_desc_0 {
  size_t reserved;//保留變量大小
  size_t Block_size;//block所占用的大小
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};

可以發(fā)現(xiàn),這個結(jié)構(gòu)體被重新命名為__main_block_desc_0_DATA,默認(rèn)傳入了兩個值0和sizeof(struct __main_block_impl_0),block的底層就是__main_block_impl_0結(jié)構(gòu)體,所以這個結(jié)構(gòu)體==第二個值保存的是block的大小==

接下來我們看一下__main_block_impl_0函數(shù)的參數(shù),第一個參數(shù)是指向__main_block_func_0函數(shù)的指針,如下:

//封裝了block執(zhí)行邏輯的函數(shù)
//第一個參數(shù)是block,后面是block調(diào)用的時候傳入的參數(shù)
void __main_block_func_0(struct __main_block_impl_0 *__cself, int a, int b) {
  int age = __cself->age; // bound by copy

  NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_3f4c4a_mi_0, age);
}

現(xiàn)在我們知道了,首先__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int flags=0) : age(_age)函數(shù)有兩個參數(shù),第一個參數(shù)是__main_block_func_0函數(shù)的地址(這個函數(shù)里面封裝了我們block里面執(zhí)行的代碼),第二個參數(shù)是__main_block_desc_0結(jié)構(gòu)體的地址(這個結(jié)構(gòu)體里面有保存block的大?。?,構(gòu)造函數(shù)的返回值是個__main_block_impl_0結(jié)構(gòu)體,block底層就是__main_block_impl_0結(jié)構(gòu)體,最后再獲取__main_block_impl_0結(jié)構(gòu)體的地址,賦值給左邊的“block”變量,然后我們拿到“block”變量就可以做其他事情了,至此,block定義完成。

block底層調(diào)用
//block底層調(diào)用 
block->FuncPtr(block, 10, 10);

這句代碼就很簡單了,直接取出block里面的FuncPtr函數(shù),傳入?yún)?shù)進(jìn)行調(diào)用。

不應(yīng)該是通過“block-> impl->FuncPtr(block, 10, 10)”來拿到FuncPtr嗎?其實我們在簡化之前,代碼是這樣的:

((__block_impl *)block)->FuncPtr)((__block_impl *)block, 10, 10);

可以發(fā)現(xiàn)系統(tǒng)把block強轉(zhuǎn)成__block_impl類型的了,由于impl又是__main_block_impl_0結(jié)構(gòu)體的第一個成員,所以impl的地址和__main_block_impl_0結(jié)構(gòu)體的地址是一樣的,強轉(zhuǎn)之后可以直接獲取到FuncPtr。

根據(jù)如上分析,驗證了,block是封裝了函數(shù)調(diào)用以及函數(shù)調(diào)用環(huán)境的OC對象

__main_block_impl_0``__block_impl__main_block_desc_03個結(jié)構(gòu)體之間的關(guān)系

block的本質(zhì)

咋們通過梳理了block底層幾個類的關(guān)系,現(xiàn)在調(diào)整一下代碼

struct __main_block_desc_0 {
    size_t reserved;
    size_t Block_size;
};

struct __block_impl {
    void *isa;
    int Flags;
    int Reserved;
    void *FuncPtr;
};

struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    int age;
};

int main(int argc, const char * argv[]) {
    @autoreleasepool {

        
        int age = 20;

        void (^block)(int, int) =  ^(int a , int b){
            NSLog(@"this is a block! -- %d", age);
        };



        struct __main_block_impl_0 *blockStruct = (__bridge struct __main_block_impl_0 *)block;



        block(10, 10);
    }
    return 0;
}

加斷點,驗證一下內(nèi)存

image-20210422161112236

FuncPtr地址驗證

image-20210422161234946

總結(jié):

如上圖所示,block底層就是一個__main_block_impl_0結(jié)構(gòu)體,它由三個部分組成:

  1. 第一部分是impl,它是個結(jié)構(gòu)體,里面有isa指針和FuncPtr指針,F(xiàn)uncPtr指針指向__main_block_func_0函數(shù),這個函數(shù)里面封裝了block需要執(zhí)行的代碼。
  2. 第二部分是desc,它是個指針,指向__main_block_desc_0結(jié)構(gòu)體,它里面有一個Block_size用來保存block的大小。
  3. 第三部分是age,它把外面訪問的成員變量age封裝到自己里面了。

block的變量捕獲(capture)

image-20210422161702137
  1. 如果是被auto修飾的局部變量,會被捕獲,是值傳遞
  2. 如果是被static修飾的局部變量,會被捕獲,是指針傳遞
  3. 如果是全局變量,不會被捕獲,因為可以直接訪問
一:auto變量
  • auto變量:自動變量,離開作用域就會銷毀,一般我們創(chuàng)建的局部變量都是auto變量,比如 int age = 10,系統(tǒng)會在默認(rèn)在前面加上auto int age = 10
    首先我們要搞清楚,什么是捕獲,所謂捕獲外部變量,意思就是在block內(nèi)部,創(chuàng)建一個變量來存放外部變量,這就叫做捕獲.
int main(int argc, const char * argv[]) {
    @autoreleasepool {
       int age = 10;
        void (^block)(void) = ^{
            NSLog(@"age is %d",age);
        };
        age = 20;
        block();
    }
    return 0;
}
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m
{
        int age = 10;
       //定義block
        void (*block)(void) = &__main_block_impl_0(
                                                   __main_block_func_0,
                                                   &__main_block_desc_0_DATA,
                                                   age
                                                   );
        age = 20;
       //調(diào)用block
        block->FuncPtr(block);
    return 0;
}

我們看到在調(diào)用block的構(gòu)造函數(shù)時,傳入了三個參數(shù),分別是:__main_block_func_0,&__main_block_desc_0_DATA,age,我們找到block的構(gòu)造函數(shù),看看內(nèi)部如何處理這個age:

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int age;  // 1: 定義了一個同名的age變量
//block構(gòu)造函數(shù)
  __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;
    age = _age; //2 :C++的特殊語法,在構(gòu)造函數(shù)內(nèi)部會默認(rèn)把_age賦值給age
  }
};

通過查看block的內(nèi)部結(jié)構(gòu)看我們發(fā)現(xiàn),block內(nèi)部創(chuàng)建了一個age變量,并且在block構(gòu)造函數(shù)中,把傳遞進(jìn)來的_age賦值給了這個age變量.我們看看調(diào)用block時,他的底部取的是哪個age:

//調(diào)用block的FuncPtr函數(shù),把block當(dāng)做參數(shù)傳遞進(jìn)去
block->FuncPtr(block);
//FuncPtr函數(shù)內(nèi)部
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
//通過傳遞的block,找到block內(nèi)部的age
  int age = __cself->age; // bound by copy
//打印age
  NSLog((NSString *)&__NSConstantStringImpl__var_folders_5t_pxd6sp5x6rl9gnk21q2q934h0000gn_T_main_3089d7_mi_0,age);
        }

通過底層代碼,我們看到,在調(diào)用block時,block會找到自己內(nèi)部的age變量,然后打印數(shù)出,所以我們修改age = 20,并不會影響block內(nèi)部的age

二:static變量

我們執(zhí)行如下代碼:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
       auto int age = 10; 
       static int height = 10;

    void (^block)(void) = ^{
       // age的值捕獲進(jìn)來
       // height的指針捕獲進(jìn)來
       NSLog(@"age is %d, height is %d", age, height);
    };

      age = 20;
      height = 20;

      block();

    }
    return 0;
}

RUN>

2021-04-22 16:31:33.658732+0800 Interview01-Block的本質(zhì)[4670:177646] age is 10, height is 20
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m

代碼轉(zhuǎn)成C++代碼,抽取關(guān)鍵的代碼,如下:

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int age;
  int *height;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int *_height, int flags=0) : age(_age), height(_height) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int age = __cself->age; // bound by copy
  int *height = __cself->height; // bound by copy

  NSLog((NSString *)&__NSConstantStringImpl__var_folders_2w_t9gvrhjs7gv_m4kb_8q3r_980000gn_T_main_12eb7d_mi_1, age, (*height));
}

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 

    auto int age = 10;
    static int height = 10;

    void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age, &height));//&height傳遞指針

    age = 20;
    height = 20;

    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
    }

    return 0;
}

可以看出age和height都被捕獲了,age是值捕獲,height是指針捕獲。

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int age;//定義 age 變量
  int *height;//定義一個 指針變量,存放外部變量的指針
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int *_height, int flags=0) : age(_age), height(_height) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

定義了兩個變量age,height,不同的是,height是一個指針指針變量,用于存放外部變量的指針.我們再來看看執(zhí)行block代碼塊的內(nèi)部:

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int age = __cself->age; // bound by copy
  int *height = __cself->height; // bound by copy
 // *height : 取出指針變量所指向的內(nèi)存的值
   NSLog((NSString *)&__NSConstantStringImpl__var_folders_5t_pxd6sp5x6rl9gnk21q2q934h0000gn_T_main_bf6cae_mi_0,age,(*height));
        }

我們看到,對于age是捕獲到內(nèi)部,把外部age的值存起來,而對于height,是把外部變量的指針保存起來,所以,我們在修改height時,會影響到block內(nèi)部的值

  1. 我們定義block,其實就是初始化__main_block_impl_0結(jié)構(gòu)體,定義block的時候會把age和&height傳進(jìn)去,這個結(jié)構(gòu)體里面有一個age一個height指針用于接收傳進(jìn)去的值,這行代碼“age(_age), height(_height)”就是保證外面的變量改變的時候?qū)崟r改變結(jié)構(gòu)體里面的age和height指針的值。
  2. 我們調(diào)用block的時候,其實就是執(zhí)行__main_block_func_0函數(shù),這個函數(shù)會獲取__main_block_impl_0結(jié)構(gòu)體中age和height指針的值,所以打印的時候就會把age和*height的值打印出來。
  3. 所以執(zhí)行完block之后age的值沒改變,因為是值傳遞,height的值改變了,因為是指針傳遞。

為什么auto變量是值傳遞,static變量是指針傳遞呢?

因為auto是自動變量,出了作用域后會自動銷毀的,如果我們保留他的指針,就會存在訪問野指針的情況

//定義block類型
void(^block)(void);

void test(){
    int age = 10;
    static int height = 20;
//在block內(nèi)部訪問 age , height
    block = ^{
        NSLog(@"age is %d, height is %d",age,height);
    };
    age = 20;
    height = 20;
}

//在main函數(shù)中調(diào)用
int main(int argc, const char * argv[]) {
        test();
 //test調(diào)用后,age變量就會自動銷毀,如果block內(nèi)部是保留age變量的指針,那么我們在調(diào)用block()時,就出現(xiàn)訪問野指針
        block();
}
三:全局變量

全局變量哪里都可以訪問,所以block內(nèi)部是不會捕獲全局變量的,直接訪問

int age_ = 10;
static int height_ = 10;

void (^block)(void);

void test()
{


    block = ^{
        // age的值捕獲進(jìn)來(capture)
        NSLog(@"age is %d, height is %d", age, height);
    };

    age = 20;
    height = 20;
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        test();
        block();
    }
    return 0;
}

轉(zhuǎn)成C++文件之后,代碼如下:

int age_ = 10;
static int height_ = 10;

void (*block)(void);

struct __test_block_impl_0 {
  struct __block_impl impl;
  struct __test_block_desc_0* Desc;
  __test_block_impl_0(void *fp, struct __test_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

static void __test_block_func_0(struct __test_block_impl_0 *__cself) {


        NSLog((NSString *)&__NSConstantStringImpl__var_folders_yh_qjzhl57s63j2m9l4frv27zjc0000gn_T_main_478a1b_mi_0, age, height);
    }

static struct __test_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __test_block_desc_0_DATA = { 0, sizeof(struct __test_block_impl_0)};

void test()
{
    block = ((void (*)())&__test_block_impl_0((void *)__test_block_func_0, &__test_block_desc_0_DATA));
    age = 20;
    height = 20;
}

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        test();
        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
    }
    return 0;
}
image-20210422165620199

為什么全局變量不需要捕獲?

因為全局變量無論哪個函數(shù)都可以訪問,block內(nèi)部當(dāng)然也可以正常訪問,所以根本無需捕獲

為什么局部變量就需要捕獲呢?

因為作用域的問題,我們在一個函數(shù)中定義變量,在block內(nèi)部訪問,本質(zhì)上跨函數(shù)訪問,所以需要捕獲起來.

四:self的捕獲
#import <Foundation/Foundation.h>

@interface MJPerson : NSObject

@property (copy, nonatomic) NSString *name;

- (void)test;

- (instancetype)initWithName:(NSString *)name;

@end
    
#import "MJPerson.h"

@implementation MJPerson

- (void)test
{
    void (^block)(void) = ^{
        NSLog(@"-------%d", [self name]);
    };
    block();
}

- (instancetype)initWithName:(NSString *)name
{
    if (self = [super init]) {
        self.name = name;
    }
    return self;
}

@end

MJPerson.m轉(zhuǎn)成C++代碼:

struct __MJPerson__test_block_impl_0 {
  struct __block_impl impl;
  struct __MJPerson__test_block_desc_0* Desc;
  MJPerson *self;
  __MJPerson__test_block_impl_0(void *fp, struct __MJPerson__test_block_desc_0 *desc, MJPerson *_self, int flags=0) : self(_self) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

static void __MJPerson__test_block_func_0(struct __MJPerson__test_block_impl_0 *__cself) {
  MJPerson *self = __cself->self; // bound by copy

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_MJPerson_1027e6_mi_0, ((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("name")));
    }

static void _I_MJPerson_test(MJPerson * self, SEL _cmd) {
    void (*block)(void) = ((void (*)())&__MJPerson__test_block_impl_0((void *)__MJPerson__test_block_func_0, &__MJPerson__test_block_desc_0_DATA, self, 570425344));
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}

static instancetype _I_MJPerson_initWithName_(MJPerson * self, SEL _cmd, NSString *name) {
    if (self = ((MJPerson *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("MJPerson"))}, sel_registerName("init"))) {
        ((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)self, sel_registerName("setName:"), (NSString *)name);
    }
    return self;
}
image-20210422171347998

可以看出self被捕獲了,并且是指針捕獲,既然被捕獲,就說明self是局部變量

為什么self是局部變量呢?
其實每個方法都兩個隱式參數(shù),一個是self一個是_cmd,self是方法調(diào)用者,_cmd是方法名,既然self被當(dāng)做參數(shù)了,那self肯定是局部變量了,也可以在上面的代碼中進(jìn)行驗證,我們看一下轉(zhuǎn)換后的test()方法:

static void _I_Person_test(Person * self, SEL _cmd) {
    void(*block)(void) = ((void (*)())&__Person__test_block_impl_0((void *)__Person__test_block_func_0, &__Person__test_block_desc_0_DATA, self, 570425344));
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}

OC 中的test()方法時沒有參數(shù)的,但是轉(zhuǎn)換成 C++ 后就多了兩個參數(shù)self,_cmd,其實我們每個 OC 方法都會默認(rèn)有這兩個參數(shù),這也是為什么我們在每個方法中都能訪問self_cmd,而參數(shù)就是局部變量,所以block就自然而然的捕獲了self.

對于[self name],在上面的代碼可以看出是給self發(fā)送消息,如下:

objc_msgSend((id)self, sel_registerName("name"))

所以,block會捕獲self,如果想要訪問self中的成員變量就給self發(fā)送消息就好了(self都被捕獲了,肯定可以獲取到self中的其他信息了)。

總結(jié):

一:只要是局部變量,不管是auto 變量,還是static 變量,block都會捕獲.不同的是,對于auto 變量,block是保存值,而static 變量 是保存的指針.
二:如果是全局變量,根本不需要捕獲,直接訪問。

特別備注

本系列文章總結(jié)自MJ老師在騰訊課堂iOS底層原理班(下)/OC對象/關(guān)聯(lián)對象/多線程/內(nèi)存管理/性能優(yōu)化,相關(guān)圖片素材均取自課程中的課件。如有侵權(quán),請聯(lián)系我刪除,謝謝!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容