Block詳解

目錄

  • 1.Block 的基本使用
  • 2.Block 的底層數(shù)據(jù)結(jié)構(gòu)
  • 3.Block 的變量捕獲機(jī)制
    3.1 auto 類型的局部變量
    3.2 static 類型的局部變量
    3.3 全局變量
    3.4 對象類型的 auto 變量
    3.5 __block 修飾的變量
    ?3.5.1 __block 作用
    ?3.5.2 __block 修飾符
    ?3.5.3 __block 的內(nèi)存管理
    ?3.5.4 __block 的 __forwarding 指針
    ?3.5.5 對象類型的 auto 變量肆汹、__block 變量內(nèi)存管理區(qū)別
    ?3.5.6 被 __block 修飾的對象類型
  • 4.Block 的類型
  • 5.Block 的 copy
  • 6.Block 的循環(huán)引用問題
    6.1 ARC
    6.2 MRC
  • 7.相關(guān)面試題

1.Block 的使用

Block 是什么谓谦?

代碼塊鳍怨,封裝了函數(shù)調(diào)用以及調(diào)用環(huán)境的 OC 對象选泻,

Block 的聲明

// 1.
@property (nonatomic, copy) void(^myBlock1)(void);
// 2.BlockType:類型別名
typedef void(^BlockType)(void);
@property (nonatomic, copy) BlockType myBlock2;
// 3.
    // 返回值類型(^block變量名)(參數(shù)1類型,參數(shù)2類型,...)
    void(^block)(void);

Block 的定義

    // ^返回值類型(參數(shù)1,參數(shù)2,...){};
    // 1.無返回值乾翔,無參數(shù)
    void(^block1)(void) = ^{

    };
    // 2.無返回值,有參數(shù)
    void(^block2)(int) = ^(int a){

    };
    // 3.有返回值箍镜,無參數(shù)(不管有沒有返回值鱼鸠,定義的返回值類型都可以省略)
    int(^block3)(void) = ^int{
        return 3;
    };
    // 以上Block的定義也可以這樣寫:
    int(^block4)(void) = ^{
        return 3;
    };
    // 4.有返回值,有參數(shù)
    int(^block5)(int) = ^int(int a){
        return 3 * a;
    };

Block 的調(diào)用

    // 1.無返回值稳衬,無參數(shù)
    block1();
    // 2.有返回值霞捡,有參數(shù)
    int a = block5(2);

使用示例

    int multiplier = 7;
    int (^myBlock)(int) = ^(int num) {
        return num * multiplier;
    };

    printf("%d", myBlock(3));
    // prints "21"

2.Block 的底層數(shù)據(jù)結(jié)構(gòu)

  • Block 本質(zhì)上也是一個(gè) OC 對象,它內(nèi)部也有個(gè)isa指針薄疚;
  • Block 是封裝了函數(shù)調(diào)用以及調(diào)用環(huán)境的 OC 對象碧信;
  • Block 的底層數(shù)據(jù)結(jié)構(gòu)如下圖所示:
image

通過 Clang 將以下 Block 代碼轉(zhuǎn)換為 C++ 代碼赊琳,來分析 Block 的底層實(shí)現(xiàn)。

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

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

        void(^block)(void) = ^{
            NSLog(@"調(diào)用了block");
        };
        block();
    }
    return 0;
}

  • Block 底層數(shù)據(jù)結(jié)構(gòu)就是一個(gè)__main_block_impl_0結(jié)構(gòu)體對象砰碴,其中有__block_impl__main_block_desc_0兩個(gè)結(jié)構(gòu)體對象成員躏筏。
    main:表示 block 所在的函數(shù)
    block:表示這個(gè)一個(gè) block
    impl:表示實(shí)現(xiàn)(implementation)
    0:表示這是該函數(shù)中的第一個(gè) block
  • __main_block_func_0結(jié)構(gòu)體封裝了 block 里的代碼;
  • __block_impl結(jié)構(gòu)體才是真正定義 block 的結(jié)構(gòu)呈枉,其中的FuncPtr指針指向__main_block_func_0趁尼;
  • __main_block_desc_0是 block 的描述對象,存儲(chǔ)著 block 的內(nèi)存大小等猖辫;
  • 定義 block 的本質(zhì):
    調(diào)用__main_block_impl_0()構(gòu)造函數(shù)酥泞,并且給它傳了兩個(gè)參數(shù)__main_block_func_0&__main_block_desc_0_DATA。拿到函數(shù)的返回值啃憎,再取返回值的地址&__main_block_impl_0芝囤,把這個(gè)地址賦值給 block 變量。
  • 調(diào)用 block 的本質(zhì):
    通過__main_block_impl_0中的__block_impl中的FuncPtr拿到函數(shù)地址辛萍,直接調(diào)用悯姊。
// main.cpp
struct __main_block_impl_0 {
    struct __block_impl impl;         // block的結(jié)構(gòu)體
    struct __main_block_desc_0* Desc; // block的描述對象,描述block的大小等
    /*  構(gòu)造函數(shù)
     ** 返回值:__main_block_impl_0 結(jié)構(gòu)體
     ** 參數(shù)一:__main_block_func_0 結(jié)構(gòu)體
     ** 參數(shù)二:__main_block_desc_0 結(jié)構(gòu)體的地址
     ** 參數(shù)三:flags 標(biāo)識位
     */
    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
        impl.isa = &_NSConcreteStackBlock; //_NSConcreteStackBlock 表示block存在棧上
        impl.Flags = flags; 
        impl.FuncPtr = fp;
        Desc = desc;
    }
};

// __main_block_func_0 封裝了block里的代碼
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_77_f_d18dtx6277bxbcd8s72my80000gn_T_main_58a448_mi_0);
}

struct __block_impl {
    void *isa;     // block的類型
    int Flags;     // 標(biāo)識位
    int Reserved;  // 
    void *FuncPtr; // block的執(zhí)行函數(shù)指針贩毕,指向__main_block_func_0
};

static struct __main_block_desc_0 {
    size_t reserved;
    size_t Block_size; // block本質(zhì)結(jié)構(gòu)體所占內(nèi)存空間
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)}; 

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        /*
          ** void(^block)(void) = ^{
                 NSLog(@"調(diào)用了block");
             };
         ** 定義block的本質(zhì):
         ** 調(diào)用__main_block_impl_0()構(gòu)造函數(shù)
         ** 并且給它傳了兩個(gè)參數(shù) __main_block_func_0 和 &__main_block_desc_0_DATA
         ** __main_block_func_0 封裝了block里的代碼
         ** 拿到函數(shù)的返回值悯许,再取返回值的地址 &__main_block_impl_0,
         ** 把這個(gè)地址賦值給 block
         */
        void(*block)(void) = ((void (*)())&__main_block_impl_0(
                                                               (void *)__main_block_func_0,
                                                               &__main_block_desc_0_DATA
                                                              ));
        /*
         ** block();
         ** 調(diào)用block的本質(zhì):
         ** 通過 __main_block_impl_0 中的 __block_impl 中的 FuncPtr 拿到函數(shù)地址辉阶,直接調(diào)用
         */      
        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
    }
    return 0;
}

3.Block 的變量捕獲機(jī)制

為了保證 block 內(nèi)部能夠正常訪問外部的變量岸晦,block 有個(gè)變量捕獲機(jī)制掩缓。

  • 對于全局變量讹蘑,不會(huì)捕獲到 block 內(nèi)部则北,訪問方式為直接訪問

  • 對于 auto 類型的局部變量店印,會(huì)捕獲到 block 內(nèi)部,block 內(nèi)部會(huì)自動(dòng)生成一個(gè)成員變量倒慧,用來存儲(chǔ)這個(gè)變量的值按摘,訪問方式為值傳遞

  • 對于 static 類型的局部變量纫谅,會(huì)捕獲到 block 內(nèi)部炫贤,block 內(nèi)部會(huì)自動(dòng)生成一個(gè)成員變量,用來存儲(chǔ)這個(gè)變量的地址付秕,訪問方式為指針傳遞兰珍;

  • 對于對象類型的局部變量,block 會(huì)連同它的所有權(quán)修飾符一起捕獲询吴。

    image

3.1 auto 類型的局部變量

auto 自動(dòng)變量:我們定義出來的變量掠河,默認(rèn)都是 auto 類型亮元,只是省略了。

    auto int age = 10唠摹;

auto 類型的局部變量會(huì)捕獲到 block 內(nèi)部爆捞,訪問方式為值傳遞

通過 Clang 將以下代碼轉(zhuǎn)換為 C++ 代碼:

    int age = 10;
    void(^block)(void) = ^{
        NSLog(@"%d",age);
    };
    block();
  • __main_block_impl_0對象內(nèi)部會(huì)生成一個(gè)相同的age變量勾拉;
  • __main_block_impl_0()構(gòu)造函數(shù)多了個(gè)參數(shù)煮甥,用來捕獲訪問的外面的age變量的,將它賦值給__main_block_impl_0對象內(nèi)部的age變量藕赞。
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;
    }
};

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
    int age = __cself->age; // bound by copy
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_77_f_d18dtx6277bxbcd8s72my80000gn_T_main_5ed490_mi_0,age);
}
......

    int age = 10;
    void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age));
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);

由于是值傳遞成肘,我們修改外部的age變量的值,不會(huì)影響到 block 內(nèi)部的age變量找默。

    int age = 10;
    void(^block)(void) = ^{
        NSLog(@"%d",age);
    };
    age = 20;
    block();
    // 10

3.2 static 類型的局部變量

static 類型的局部變量會(huì)捕獲到 block 內(nèi)部艇劫,訪問方式為指針傳遞

通過 Clang 將以下代碼轉(zhuǎn)換為 C++ 代碼:

    static int age = 10;
    void(^block)(void) = ^{
        NSLog(@"%d",age);
    };
    block();

  • __main_block_impl_0對象內(nèi)部會(huì)生成一個(gè)相同類型的age指針惩激;
  • __main_block_impl_0()構(gòu)造函數(shù)多了個(gè)參數(shù)店煞,用來捕獲訪問的外面的age變量的地址,將它賦值給__main_block_impl_0對象內(nèi)部的age指針风钻。
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;
    }
};

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
    int *age = __cself->age; // bound by copy
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_77_f_d18dtx6277bxbcd8s72my80000gn_T_main_a4bc7d_mi_0,(*age));
}

......

    static int age = 10;
    void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &age));
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);

由于是指針傳遞顷蟀,我們修改外部的age變量的值,會(huì)影響到 block 內(nèi)部的age變量骡技。

    static int age = 10;
    void(^block)(void) = ^{
        NSLog(@"%d",age);
    };
    age = 20;
    block();
    // 20

3.3 全局變量

全局變量不會(huì)捕獲到 block 內(nèi)部鸣个,訪問方式為直接訪問

通過 Clang 將以下代碼轉(zhuǎn)換為 C++ 代碼:

int _age = 10;
static int _height = 20;
......
        void(^block)(void) = ^{
            NSLog(@"%d,%d",_age,_height);
        };
        block();

  • __main_block_impl_0對象內(nèi)并沒有生成對應(yīng)的變量布朦,也就是說全局變量沒有捕獲到 block 內(nèi)部囤萤,而是直接訪問。
int _age = 10;
static int _height = 20;

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;
    }
};

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_77_f_d18dtx6277bxbcd8s72my80000gn_T_main_12efa5_mi_0,_age,_height);
}

......

    void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);

為什么局部變量需要捕獲是趴,全局變量不用捕獲呢涛舍?

  • 作用域的原因,全局變量哪里都可以直接訪問唆途,所以不用捕獲富雅;
  • 局部變量,外部不能直接訪問肛搬,所以需要捕獲没佑;
  • auto 類型的局部變量可能會(huì)銷毀,其內(nèi)存會(huì)消失温赔,block 將來執(zhí)行代碼的時(shí)候不可能再去訪問那塊內(nèi)存蛤奢,所以捕獲其值;
  • static 變量會(huì)一直保存在內(nèi)存中, 所以捕獲其地址即可远剩。

3.4 對象類型的 auto 變量

當(dāng) block 內(nèi)部訪問了對象類型的 auto 變量時(shí):

  • 如果 block 是在棧上扣溺,將不會(huì)對 auto 變量產(chǎn)生強(qiáng)引用
  • 如果 block 被拷貝到堆上
    ① block 內(nèi)部的 desc 結(jié)構(gòu)體會(huì)新增兩個(gè)函數(shù):
    ?copy(__main_block_copy_0,函數(shù)名命名規(guī)范同__main_block_impl_0
    ?dispose(__main_block_dispose_0
    ② 會(huì)調(diào)用 block 內(nèi)部的 copy 函數(shù)
    ③ copy 函數(shù)內(nèi)部會(huì)調(diào)用_Block_object_assign函數(shù)
    _Block_object_assign函數(shù)會(huì)根據(jù) auto 變量的修飾符(__strong瓜晤、__weak锥余、__unsafe_unretained)做出相應(yīng)的操作,形成強(qiáng)引用(retain)或者弱引用
  • 如果 block 從堆上移除
    ① 會(huì)調(diào)用 block 內(nèi)部的 dispose 函數(shù)
    ② dispose 函數(shù)內(nèi)部會(huì)調(diào)用_Block_object_dispose函數(shù)
    _Block_object_dispose函數(shù)會(huì)自動(dòng)釋放引用的 auto 變量(release)
函數(shù) 調(diào)用時(shí)機(jī)
copy 函數(shù) 棧上的 block 復(fù)制到堆時(shí)
dispose 函數(shù) 堆上的 block 被廢棄時(shí)

如下代碼痢掠,block 保存在堆中驱犹,當(dāng)執(zhí)行完作用域2的時(shí)候,Person 對象并沒有被釋放足画,而是在執(zhí)行完作用域1的時(shí)候釋放雄驹,說明 block 內(nèi)部對 Person 對象產(chǎn)生了強(qiáng)引用。

typedef void(^MyBlock)(void);

int main(int argc, const char * argv[]) {
    @autoreleasepool { //作用域1
        MyBlock block;
        { //作用域2
            Person *p = [Person new];
            p.name = @"zhangsan";      
            block = ^{
                NSLog(@"%@",p.name);
            };
        }
        NSLog(@"-----");
    }
    return 0;
}
// -----
// Person-dealloc

通過 Clang 將以上代碼轉(zhuǎn)換為 C++ 代碼:

// 弱引用需要運(yùn)行時(shí)的支持淹辞,所以需要加上 -fobjc-arc -fobjc-runtime=ios-8.0.0
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m

__main_block_impl_0中生成了一個(gè)Person *__strong p指針医舆,指向外面的 person 對象,且是強(qiáng)引用象缀。

typedef void(*MyBlock)(void);

struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    Person *__strong p; // 強(qiáng)引用
    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *_p, int flags=0) : p(_p) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
    }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
    Person *__strong p = __cself->p; // bound by copy
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_77_f_d18dtx6277bxbcd8s72my80000gn_T_main_9e5699_mi_1,((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)p, sel_registerName("name")));
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->p, (void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static struct __main_block_desc_0 {
    size_t reserved;
    size_t Block_size;
    void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
    void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        MyBlock block;
        {
            Person *p = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("new"));
            ((void (*)(id, SEL, NSString * _Nonnull))(void *)objc_msgSend)((id)p, sel_registerName("setName:"), (NSString *)&__NSConstantStringImpl__var_folders_77_f_d18dtx6277bxbcd8s72my80000gn_T_main_9e5699_mi_0);

            block = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, p, 570425344));
        }
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_77_f_d18dtx6277bxbcd8s72my80000gn_T_main_9e5699_mi_2);
    }
    return 0;
}

添加了__weak修飾后蔬将,當(dāng)執(zhí)行完作用域2的時(shí)候,Person 對象就被被釋放了央星。

typedef void(^MyBlock)(void);

int main(int argc, const char * argv[]) {
    @autoreleasepool { //作用域1
        MyBlock block;
        { //作用域2
            __weak Person *p = [Person new];
            p.name = @"zhangsan";      
            block = ^{
                NSLog(@"%@",p.name);
            };
        }
        NSLog(@"-----");
    }
    return 0;
}
// Person-dealloc
// -----

同樣的霞怀,通過 Clang 將以上代碼轉(zhuǎn)換為 C++ 代碼。
__main_block_impl_0中生成了一個(gè)Person *__weak p指針莉给,指向外面的 person 對象毙石,且是弱引用。
說明當(dāng) block 內(nèi)部 訪問了對象類型的 auto 變量時(shí)颓遏,如果 block 被拷貝到堆上徐矩,會(huì)連同對象的所有權(quán)修飾符一起捕獲。

......
struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    Person *__weak p; //弱引用
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *__weak _p, int flags=0) : p(_p) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
    }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
    Person *__weak p = __cself->p; // bound by copy
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_77_f_d18dtx6277bxbcd8s72my80000gn_T_main_c61841_mi_1,((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)p, sel_registerName("name")));
}
......

3.5 __block 修飾的變量

3.5.1 __block 作用

默認(rèn)情況下 block 是不能修改外面的 auto 變量的叁幢,解決辦法丧蘸?

  • 變量用 static 修飾(原因:捕獲 static 類型的局部變量是指針傳遞,可以訪問到該變量的內(nèi)存地址)
  • 全局變量
  • __block(我們只希望臨時(shí)用一下這個(gè)變量臨時(shí)改一下而已遥皂,而改為 static 變量和全局變量會(huì)一直在內(nèi)存中)

3.5.2 __block 修飾符

  • __block 可以用于解決 block 內(nèi)部無法修改 auto 變量值的問題;
  • __block 不能修飾全局變量刽漂、靜態(tài)變量演训;
  • 編譯器會(huì)將 __block 變量包裝成一個(gè)對象(struct __Block_byref_age_0(byref:按地址傳遞));
  • 加 __block 修飾不會(huì)修改變量的性質(zhì)贝咙,它還是 auto 變量样悟;
  • 一般情況下,對被捕獲變量進(jìn)行賦值(賦值!=使用)操作需要添加 __block 修飾符。比如給數(shù)組添加或者刪除對象窟她,就不用加 __block 修飾陈症;
  • 在 MRC 下使用 __block 修飾對象類型,在 block 內(nèi)部不會(huì)對該對象進(jìn)行 retain 操作震糖,所以在 MRC 環(huán)境下可以通過 __block 解決循環(huán)引用的問題录肯。

使用示例

    __block int age = 10;
    void(^block)(void) = ^{
        age = 20;
        NSLog(@"block-%d",age);
    };
    block();
    NSLog(@"%d",age);
    // block-20
    // 20

通過 Clang 將以上代碼轉(zhuǎn)換為 C++ 代碼。

  • 編譯器會(huì)將 __block 修飾的變量包裝成一個(gè)__Block_byref_age_0對象吊说;
  • 以上age = 20;的賦值過程為:通過 block 結(jié)構(gòu)體里的(__Block_byref_age_0)類型的 age 指針论咏,找到__Block_byref_age_0結(jié)構(gòu)體的內(nèi)存(即被 __block 包裝成對象的內(nèi)存),把__Block_byref_age_0結(jié)構(gòu)體里的 age 變量的值改為20颁井。
  • 由于編譯器將 __block 變量包裝成了一個(gè)對象厅贪,所以它的內(nèi)存管理幾乎等同于訪問對象類型的 auto 變量,但還是有差異雅宾,下面會(huì)講到养涮。
struct __Block_byref_age_0 {
    void *__isa;
    __Block_byref_age_0 *__forwarding;
    int __flags;
    int __size;
    int age; 
};

struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    __Block_byref_age_0 *age; // by ref
    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_age_0 *_age, int flags=0) : age(_age->__forwarding) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
    }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
    __Block_byref_age_0 *age = __cself->age; // bound by ref
    (age->__forwarding->age) = 20;
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_77_f_d18dtx6277bxbcd8s72my80000gn_T_main_9578d0_mi_0,(age->__forwarding->age));
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->age, (void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);}

static struct __main_block_desc_0 {
    size_t reserved;
    size_t Block_size;
    void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
    void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        __attribute__((__blocks__(byref))) __Block_byref_age_0 age = {(void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 10};
        void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_age_0 *)&age, 570425344));
        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_77_f_d18dtx6277bxbcd8s72my80000gn_T_main_9578d0_mi_1,(age.__forwarding->age));
    }
    return 0;
}

3.5.3 __block 的內(nèi)存管理

  • 當(dāng) block 在棧上時(shí),并不會(huì)對 __block 變量產(chǎn)生強(qiáng)引用

  • 當(dāng) block 被 copy 到堆時(shí)
    ① block 內(nèi)部的 desc 結(jié)構(gòu)體會(huì)新增兩個(gè)函數(shù):
    ?copy(__main_block_copy_0眉抬,函數(shù)名命名規(guī)范同__main_block_impl_0
    ?dispose(__main_block_dispose_0
    ② 會(huì)調(diào)用 block 內(nèi)部的 copy 函數(shù)
    ③ copy 函數(shù)內(nèi)部會(huì)調(diào)用_Block_object_assign函數(shù)
    _Block_object_assign函數(shù)會(huì)對 __block 變量形成強(qiáng)引用(retain)

  • 當(dāng) block 從堆中移除時(shí)
    ① 會(huì)調(diào)用 block 內(nèi)部的 dispose 函數(shù)
    ② dispose 函數(shù)內(nèi)部會(huì)調(diào)用_Block_object_dispose函數(shù)
    _Block_object_dispose函數(shù)會(huì)自動(dòng)釋放引用的 __block 變量(release)

    image

3.5.4 __block 的 __forwarding 指針

__block 的__forwarding指針存在的意義贯吓?
為什么要通過 age 結(jié)構(gòu)體里的__forwarding指針拿到 age 變量的值,而不直接 age 結(jié)構(gòu)體拿到 age 變量的值呢吐辙?

__block 的__forwarding是指向自己本身的指針宣决,為了不論在任何內(nèi)存位置,都可以順利的訪問同一個(gè) __block 變量昏苏。

  • block 對象 copy 到堆上時(shí)尊沸,內(nèi)部的 __block 變量也會(huì) copy 到堆上去。為了防止 age 的值賦值給棧上的 __block 變量贤惯,就使用了__forwarding洼专;
  • 當(dāng) __block 變量在棧上的時(shí)候,__block 變量的結(jié)構(gòu)體中的__forwarding指針指向自己孵构,這樣通過__forwarding取到結(jié)構(gòu)體中的 age 給它賦值沒有問題屁商;
  • 當(dāng) __block 變量 copy 到堆上后,棧上的__forwarding指針會(huì)指向 copy 到堆上的 _block 變量結(jié)構(gòu)體颈墅,而堆上的__forwarding指向自己蜡镶;

這樣不管我們訪問的是棧上還是堆上的 __block 變量結(jié)構(gòu)體,只要是通過__forwarding指針訪問恤筛,都是訪問到堆上的 __block 變量結(jié)構(gòu)體官还;給 age 賦值,就肯定會(huì)賦值給堆上的那個(gè) __block 變量中的 age毒坛。

image

3.5.5 對象類型的 auto 變量望伦、__block 變量內(nèi)存管理區(qū)別

  • 當(dāng) block 在棧上時(shí)林说,對它們都不會(huì)產(chǎn)生強(qiáng)引用
  • 當(dāng) block 拷貝到堆上時(shí),都會(huì)通過 copy 函數(shù)來處理它們
image.png
  • 當(dāng) block 從堆上移除時(shí)屯伞,都會(huì)通過 dispose 函數(shù)來釋放它們
image.png

3.5.6 被 __block 修飾的對象類型

  • 當(dāng) __block 變量在棧上時(shí)腿箩,不會(huì)對指向的對象產(chǎn)生強(qiáng)引用
  • 當(dāng) __block 變量被 copy 到堆時(shí)
    __Block_byref_object_0即 __block 變量內(nèi)部會(huì)新增兩個(gè)函數(shù):
    ?copy(__Block_byref_id_object_copy
    ?dispose(__Block_byref_id_object_dispose
    ② 會(huì)調(diào)用 __block 變量內(nèi)部的 copy 函數(shù)
    ③ copy 函數(shù)內(nèi)部會(huì)調(diào)用_Block_object_assign函數(shù)
    _Block_object_assign函數(shù)會(huì)根據(jù)所指向?qū)ο蟮男揎椃?code>__strong、__weak劣摇、__unsafe_unretained)做出相應(yīng)的操作珠移,形成強(qiáng)引用(retain)或者弱引用(注意:這里僅限于 ARC 時(shí)會(huì) retain,MRC 時(shí)不會(huì) retain)
  • 如果 __block 變量從堆上移除
    ① 會(huì)調(diào)用 __block 變量內(nèi)部的 dispose 函數(shù)
    ② dispose 函數(shù)內(nèi)部會(huì)調(diào)用_Block_object_dispose函數(shù)
    _Block_object_dispose函數(shù)會(huì)自動(dòng)釋放指向的對象(release)
int main(int argc, char * argv[]) {
    NSString * appDelegateClassName;
    @autoreleasepool {
        // Setup code that might create autoreleased objects goes here.
        appDelegateClassName = NSStringFromClass([AppDelegate class]);

        __block NSObject *object = [[NSObject alloc] init];
        void(^block)(void) = ^{
            object = [[NSObject alloc] init];
        };
    }
    return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}

struct __Block_byref_object_0 {
    void *__isa;
    __Block_byref_object_0 *__forwarding;
    int __flags;
    int __size;
    void (*__Block_byref_id_object_copy)(void*, void*); // copy
    void (*__Block_byref_id_object_dispose)(void*);     // dispose
    NSObject *__strong object;
};

struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    __Block_byref_object_0 *object; // by ref
    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_object_0 *_object, int flags=0) : object(_object->__forwarding) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
    }
};

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
    __Block_byref_object_0 *object = __cself->object; // bound by ref
    (object->__forwarding->object) = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->object, (void*)src->object, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->object, 8/*BLOCK_FIELD_IS_BYREF*/);}

static struct __main_block_desc_0 {
    size_t reserved;
    size_t Block_size;
    void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
    void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};

int main(int argc, char * argv[]) {
    NSString * appDelegateClassName;
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        appDelegateClassName = NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class")));

        __attribute__((__blocks__(byref))) __Block_byref_object_0 object = {
            (void*)0,
            (__Block_byref_object_0 *)&object, 
            33554432, 
            sizeof(__Block_byref_object_0), 
            __Block_byref_id_object_copy_131,
            __Block_byref_id_object_dispose_131, 
            ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"))
        };
        void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_object_0 *)&object, 570425344));
    }
    return UIApplicationMain(argc, argv, __null, appDelegateClassName);
}

static void __Block_byref_id_object_copy_131(void *dst, void *src) {
    // __block 對象結(jié)構(gòu)體的地址+40個(gè)字節(jié)饵撑,即為結(jié)構(gòu)體中 object 對象的地址
    _Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
}
static void __Block_byref_id_object_dispose_131(void *src) {
    _Block_object_dispose(*(void * *) ((char*)src + 40), 131);
}

注意:在 MRC 下使用 __block 修飾對象類型剑梳,在 block 內(nèi)部不會(huì)對該對象進(jìn)行 retain 操作,所以在 MRC 環(huán)境下可以通過 __block 解決循環(huán)引用的問題滑潘。

示例(MRC)

// 對象類型的捕獲垢乙,連同所有權(quán)修飾符一起捕獲,所以 block 對 person 強(qiáng)引用
    HTPerson *person = [[HTPerson alloc] init];

    void(^block)(void)  = [^{
        NSLog(@"%p", person);
    } copy];

    [person release];

    block();

    [block release];

// 0x10053da30
// -[HTPerson dealloc]

// __block 修飾的對象類型的捕獲语卤,MRC 下在 block 內(nèi)部不會(huì)對該對象進(jìn)行 retain 操作
    __block HTPerson *person = [[HTPerson alloc] init];

    void(^block)(void)  = [^{
        NSLog(@"%p", person);
    } copy];

    [person release];

    block();

    [block release];

// -[HTPerson dealloc]
// 0x1007337d0

4.Block 的類型

block 有 3 種類型追逮,可以通過調(diào)用 class 方法或者 isa 指針 查看具體類型,最終都是繼承自 NSBlock 類型粹舵。

block類型 描述 環(huán)境
__ NSGlobalBlock __
( _NSConcreteGlobalBlock ) 全局block钮孵,保存在數(shù)據(jù)段 沒有訪問auto變量
__ NSStackBlock __
( _NSConcreteStackBlock ) 棧block,保存在棧區(qū) 訪問了auto變量
__ NSMallocBlock __
( _NSConcreteMallocBlock ) 堆block眼滤,保存在堆區(qū) __ NSStackBlock __調(diào)用了copy

打印各種 block 的類型巴席,以及遍歷 block 的父類類型,如下:

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

        void(^block1)(void) = ^{
            NSLog(@"hello");
        };
        /*  block2
         ** 在ARC下會(huì)自動(dòng)copy诅需,從棧復(fù)制到堆漾唉,所以為__NSMallocBlock__類型
         ** 在MRC下為__NSStackBlock__類型,需要手動(dòng)調(diào)用copy方法才會(huì)變?yōu)開_NSMallocBlock__類型
         **     同時(shí)堰塌,在不需要該block的時(shí)候需要手動(dòng)調(diào)用release方法
         */ 
        int age = 10;
        void(^block2)(void) = ^{
            NSLog(@"%d",age);
        };

        NSLog(@"%@,%@,%@", [block1 class], [block2 class], [^{
            NSLog(@"%d",age);
        } class]);

        Class class = [block1 class];
        while (class) {
            NSLog(@"%@",class);
            class = [class superclass];
        }
    }
    return 0;
}
// __NSGlobalBlock__,__NSMallocBlock__,__NSStackBlock__
// __NSGlobalBlock__
// __NSGlobalBlock
// NSBlock
// NSObject

每一種類型的 block 調(diào)用 copy 后的結(jié)果如下所示:

block類型 副本源的配置存儲(chǔ)區(qū) 復(fù)制效果
_NSConcreteGlobalBlock 程序的數(shù)據(jù)段區(qū) 什么也不做
_NSConcreteStackBlock 從棧復(fù)制到堆
_NSConcreteMallocBlock 引用計(jì)數(shù)增加

__ NSStackBlock __ 存在的問題:

以下是在 MRC 環(huán)境下赵刑,block 類型為__NSStackBlock__
當(dāng) test() 函數(shù)執(zhí)行完畢场刑,棧上的東西可能會(huì)被銷毀般此,數(shù)據(jù)就會(huì)變成垃圾數(shù)據(jù)。盡管 block 還能正常調(diào)用牵现,但是輸出的 age 的值發(fā)生了錯(cuò)亂铐懊。

void (^block)(void);
void test()
{    
    // __NSStackBlock__
    int age = 10;
    block = ^{
        NSLog(@"%d", age);
    };
}
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        test();
        block();
    }
    return 0;
}
// 272632936

解決辦法:調(diào)用copy方法,將棧 block 復(fù)制到堆瞎疼。

void (^block)(void);
void test()
{    
    // __NSMallocBlock__
    int age = 10;
    block = [^{
        NSLog(@"%d", age);
    } copy];
}
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        test();
        block();
        [block release];
    }
    return 0;
}
// 10

5.Block 的 copy

ARC 環(huán)境下科乎,編譯器會(huì)根據(jù)情況自動(dòng)將棧上的 block 復(fù)制到堆上,比如以下幾種情況:

  • 手動(dòng)調(diào)用 block 的 copy 方法時(shí)丑慎;
  • block 作為函數(shù)返回值時(shí)(Masonry 框架中用很多);
  • 將 block 賦值給__strong指針時(shí);
  • block 作為 Cocoa API 中方法名含有usingBlock的方法參數(shù)時(shí)竿裂;
  • block 作為 GCD API 的方法參數(shù)時(shí)玉吁。

block 作為屬性的寫法:

  • ARC 下寫strong或者copy都會(huì)對 block 進(jìn)行強(qiáng)引用,都會(huì)自動(dòng)將 block 從棧 copy 到堆上腻异;
  • 建議都寫成copy进副,這樣 MRC 和 ARC 下一致。
// MRC
@property (nonatomic, copy) void(^block)(void);
// ARC
@property (nonatomic, copy) void(^block)(void);
@property (nonatomic, strong) void(^block)(void);

6.Block 的循環(huán)引用問題

為什么 block 會(huì)產(chǎn)生循環(huán)引用悔常?

  • 相互循環(huán)引用:如果當(dāng)前 block 對當(dāng)前對象的某一成員變量進(jìn)行捕獲的話影斑,可能會(huì)對它產(chǎn)生強(qiáng)引用。而當(dāng)前 block 又由于當(dāng)前對象對其有一個(gè)強(qiáng)引用机打,就產(chǎn)生了相互循環(huán)引用的問題矫户;
  • 大環(huán)引用:我們?nèi)绻褂?code>__block的話,在 ARC 下可能會(huì)產(chǎn)生循環(huán)引用(MRC 則不會(huì))残邀,在 ARC 下可以通過斷環(huán)的方式去解除循環(huán)引用皆辽。但是有一個(gè)弊端,如果該 block 一直得不到調(diào)用芥挣,循環(huán)引用就一直存在驱闷。

6.1 ARC

  • __weak或者__unsafe_unretained解決:
    __weak typeof(self) weakSelf = self;
    self.block = ^{
        NSLog(@"%p",weakSelf);
    };

    __unsafe_unretained id weakSelf = self;
    self.block = ^{
        NSLog(@"%p",weakSelf);
    };

image

注意__unsafe_unretained會(huì)產(chǎn)生懸垂指針。

  • __block解決(必須要調(diào)用 block):
    缺點(diǎn):必須要調(diào)用 block空免,而且 block 里要將指針置為 nil空另。如果一直不調(diào)用 block,對象就會(huì)一直保存在內(nèi)存中蹋砚,造成內(nèi)存泄漏扼菠。
    __block id weakSelf = self;
    self.block = ^{
        NSLog(@"%p",weakSelf);
        weakSelf = nil;
    };
    self.block();

image

6.2 MRC

  • __unsafe_unretained解決:同 ARC
  • __block解決(在 MRC 下使用 __block 修飾對象類型,在 block 內(nèi)部不會(huì)對該對象進(jìn)行 retain 操作都弹,所以在 MRC 環(huán)境下可以通過 __block 解決循環(huán)引用的問題)
    __block id weakSelf = self;
    self.block = ^{
        NSLog(@"%p",weakSelf);
    };

7.相關(guān)面試題

Q:block 的本質(zhì)是什么娇豫?

封裝了函數(shù)調(diào)用以及調(diào)用環(huán)境的 OC 對象。

Q:block 的屬性修飾詞為什么是 copy畅厢?使用 block 有哪些使用注意冯痢?

block 一旦沒有進(jìn)行 copy 操作,就不會(huì)在堆上框杜。
使用注意:循環(huán)引用問題浦楣。

Q:block在給 NSMutableArray 添加或移除對象,需不需要添加 __block咪辱?

不需要振劳。

Q:block 的變量捕獲機(jī)制

block 的變量捕獲機(jī)制,是為了保證 block 內(nèi)部能夠正常訪問外部的變量油狂。

  • 對于全局變量历恐,不會(huì)捕獲到 block 內(nèi)部寸癌,訪問方式為直接訪問
  • 對于 auto 類型的局部變量弱贼,會(huì)捕獲到 block 內(nèi)部蒸苇,block 內(nèi)部會(huì)自動(dòng)生成一個(gè)成員變量,用來存儲(chǔ)這個(gè)變量的值吮旅,訪問方式為值傳遞溪烤;
  • 對于 static 類型的局部變量,會(huì)捕獲到 block 內(nèi)部庇勃,block 內(nèi)部會(huì)自動(dòng)生成一個(gè)成員變量檬嘀,用來存儲(chǔ)這個(gè)變量的地址,訪問方式為指針傳遞责嚷;
  • 對于對象類型的局部變量鸳兽,block 會(huì)連同它的所有權(quán)修飾符一起捕獲

Q:為什么局部變量需要捕獲再层,全局變量不用捕獲呢贸铜?

  • 作用域的原因,全局變量哪里都可以直接訪問聂受,所以不用捕獲蒿秦;
  • 局部變量,外部不能直接訪問蛋济,所以需要捕獲棍鳖;
  • auto 類型的局部變量可能會(huì)銷毀,其內(nèi)存會(huì)消失碗旅,block 將來執(zhí)行代碼的時(shí)候不可能再去訪問那塊內(nèi)存渡处,所以捕獲其值;
  • static 變量會(huì)一直保存在內(nèi)存中祟辟, 所以捕獲其地址即可医瘫。

Q:self 會(huì)不會(huì)捕獲到 block 內(nèi)部?

會(huì)捕獲旧困。
OC 方法都有兩個(gè)隱式參數(shù)醇份,方法調(diào)用者self和方法名_cmd
參數(shù)也是一種局部變量吼具。

Q:_name 會(huì)不會(huì)捕獲到 block 內(nèi)部僚纷?

會(huì)捕獲。
不是將_name變量進(jìn)行捕獲拗盒,而是直接將self捕獲到 block 內(nèi)部怖竭,因?yàn)?code>_name是 Person 類的成員變量,_name來自當(dāng)前的對象/方法調(diào)用者self(self->_name)陡蝇。
如果使用self.name即調(diào)用selfgetter方法痊臭,即給self對象發(fā)送一條消息哮肚,那還是要訪問到selfself是局部變量广匙,不是全局變量绽左,所以self會(huì)捕獲到 block 內(nèi)部。

Q:__ NSStackBlock __ 存在的問題:

如果沒有將 block 從棧上 copy 到堆上艇潭,那我們訪問棧上的 block 的話,可能會(huì)由于變量作用域結(jié)束導(dǎo)致棧上的 block 以及 __block 變量被銷毀戏蔑,而造成內(nèi)存崩潰蹋凝。或者數(shù)據(jù)可能會(huì)變成垃圾數(shù)據(jù)总棵,盡管將來 block 還能正常調(diào)用鳍寂,但是它捕獲的變量的值已經(jīng)錯(cuò)亂了。
解決辦法:將 block 的內(nèi)存放堆里情龄,意味著它就不會(huì)自動(dòng)銷毀迄汛,而是由我們程序員來決定什么時(shí)候銷毀它。

Q:默認(rèn)情況下 block 是不能修改外面的 auto 變量的骤视,解決辦法鞍爱?

  • 變量用 static 修飾(原因:捕獲 static 類型的局部變量是指針傳遞,可以訪問到該變量的內(nèi)存地址)
  • 全局變量
  • __block(我們只希望臨時(shí)用一下這個(gè)變量臨時(shí)改一下而已专酗,而改為 static 變量和全局變量會(huì)一直在內(nèi)存中)

Q:__block 修飾符使用注意點(diǎn):

在 MRC 下使用 __block 修飾對象類型睹逃,在 block 內(nèi)部不會(huì)對該對象進(jìn)行 retain 操作,所以在 MRC 環(huán)境下可以通過 __block 解決循環(huán)引用的問題祷肯。

Q:__block 的 __forwarding 指針存在的意義沉填?為什么要通過 age 結(jié)構(gòu)體里的 __forwarding 指針拿到 age 變量的值,而不直接 age 結(jié)構(gòu)體拿到 age 變量的值呢佑笋?

見 3.5.4 __block 的 __forwarding 指針翼闹。

Q:為什么通過 __weak 去修飾成員變量或?qū)ο缶涂梢赃_(dá)到規(guī)避循環(huán)引用的目的呢?

block 對于對象類型的局部變量連同所有權(quán)修飾符一起截獲蒋纬,所以如果我們在外部定義的對象是 __weak 所有權(quán)修飾符的猎荠,那么在 block 中所產(chǎn)生的結(jié)構(gòu)體里所持有的變量也是 __weak 類型的。

Q:解決在 block 內(nèi)部通過弱指針訪問對象成員時(shí)編譯器報(bào)錯(cuò)的問題:

    __weak typeof(self) weakSelf = self;
    self.block = ^{
        __strong typeof(weakSelf) strongSelf = weakSelf;
        NSLog(@"%d",strongSelf->age);
    };

Q:以下代碼的打印結(jié)果是颠锉?

    __block int multiplier = 6;
    int (^block)(int) = ^(int num) {
        return num * multiplier;
    };
    multiplier = 4;
    NSLog(@"%d",block(2));
    // 8

Q:以下代碼有問題嗎法牲?

    __block id weakSelf = self;
    self.block = ^{
        NSLog(@"%p",weakSelf);
    };

  • 在 MRC 下,不會(huì)產(chǎn)生循環(huán)引用琼掠;
  • 在 ARC 下拒垃,會(huì)產(chǎn)生循環(huán)引用,導(dǎo)致內(nèi)存泄漏瓷蛙,解決方案如下悼瓮。
    __block id weakSelf = self;
    self.block = ^{
        NSLog(@"%p",weakSelf);
        weakSelf = nil;
    };
    self.block();

缺點(diǎn):必須要調(diào)用 block戈毒,而且 block 里要將指針置為 nil。如果一直不調(diào)用 block横堡,對象就會(huì)一直保存在內(nèi)存中埋市,造成內(nèi)存泄漏。

作者:師大小海騰
鏈接:http://www.reibang.com/p/f8a44c366f09
來源:簡書
著作權(quán)歸作者所有命贴。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán)道宅,非商業(yè)轉(zhuǎn)載請注明出處。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末胸蛛,一起剝皮案震驚了整個(gè)濱河市污茵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌葬项,老刑警劉巖泞当,帶你破解...
    沈念sama閱讀 212,718評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異民珍,居然都是意外死亡襟士,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評論 3 385
  • 文/潘曉璐 我一進(jìn)店門嚷量,熙熙樓的掌柜王于貴愁眉苦臉地迎上來陋桂,“玉大人,你說我怎么就攤上這事蝶溶≌潞恚” “怎么了?”我有些...
    開封第一講書人閱讀 158,207評論 0 348
  • 文/不壞的土叔 我叫張陵身坐,是天一觀的道長秸脱。 經(jīng)常有香客問我,道長部蛇,這世上最難降的妖魔是什么摊唇? 我笑而不...
    開封第一講書人閱讀 56,755評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮涯鲁,結(jié)果婚禮上巷查,老公的妹妹穿的比我還像新娘。我一直安慰自己抹腿,他們只是感情好岛请,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著警绩,像睡著了一般崇败。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,050評論 1 291
  • 那天后室,我揣著相機(jī)與錄音缩膝,去河邊找鬼。 笑死岸霹,一個(gè)胖子當(dāng)著我的面吹牛疾层,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播贡避,決...
    沈念sama閱讀 39,136評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼痛黎,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了刮吧?” 一聲冷哼從身側(cè)響起舅逸,我...
    開封第一講書人閱讀 37,882評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎皇筛,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體坠七,經(jīng)...
    沈念sama閱讀 44,330評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡水醋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了彪置。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拄踪。...
    茶點(diǎn)故事閱讀 38,789評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖拳魁,靈堂內(nèi)的尸體忽然破棺而出惶桐,到底是詐尸還是另有隱情,我是刑警寧澤潘懊,帶...
    沈念sama閱讀 34,477評論 4 333
  • 正文 年R本政府宣布姚糊,位于F島的核電站,受9級特大地震影響授舟,放射性物質(zhì)發(fā)生泄漏救恨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評論 3 317
  • 文/蒙蒙 一释树、第九天 我趴在偏房一處隱蔽的房頂上張望肠槽。 院中可真熱鬧,春花似錦奢啥、人聲如沸秸仙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽寂纪。三九已至,卻和暖如春赌结,著一層夾襖步出監(jiān)牢的瞬間弊攘,已是汗流浹背抢腐。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留襟交,地道東北人迈倍。 一個(gè)月前我還...
    沈念sama閱讀 46,598評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像捣域,于是被迫代替她去往敵國和親啼染。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評論 2 351

推薦閱讀更多精彩內(nèi)容

  • 目錄1.Block 的基本使用2.Block 的底層數(shù)據(jù)結(jié)構(gòu)3.Block 的變量捕獲機(jī)制3.1 auto 類型的...
    師大小海騰閱讀 998評論 0 9
  • 第一部分:Block本質(zhì) Q:什么是Block焕梅,Block的本質(zhì)是什么迹鹅? block本質(zhì)上也是一個(gè)OC對象,它內(nèi)部...
    sheldon_龍閱讀 555評論 0 0
  • iOS開發(fā)---Block詳解 Block的基礎(chǔ) 什么是Blocks贞言? 用一句話來描述:帶有自動(dòng)變量的匿名函數(shù)(是...
    祀夢_閱讀 990評論 0 6
  • block對對象變量的捕獲 block一般使用過程中都是對對象變量的捕獲斜棚,那么對象變量的捕獲和基本數(shù)據(jù)類型變量相同...
    非洲小白猿閱讀 1,363評論 1 1
  • 窺探block底層結(jié)構(gòu) 我們寫下一個(gè)最簡單的block使用clang指令生成對應(yīng)的C\C++代碼 截取關(guān)鍵代碼如下...
    CoderKK閱讀 303評論 0 1