iOS Block简介

Block的本质

Block是将函数及其执行上下文封装起来的对象
Block调用即是函数的调用。

#import "CLBlock.h"

@implementation CLBlock
- (void)blockTest {
    int multiplier = 6;
    int(^BlockTest)(int) = ^int(int num){
        return num * multiplier;
    };
    BlockTest(2);
}
@end

clang(LLVM编译器)具有转换为我们可读源代码的功能。通过“-rewrite-objc”选项就能将含有Block语法的源代码变换为C++的源代码。说是C++,其实也仅是使用了struct结构,其本质是C语言源代码。
使用【clang -rewrite-objc CLBlock.m】进行源码解析,查看编译后的文件内容。

// @implementation CLBlock
//Block结构体
struct __CLBlock__blockTest_block_impl_0 {
  struct __block_impl impl;
  struct __CLBlock__blockTest_block_desc_0* Desc;
  int multiplier;
  //构造函数
  __CLBlock__blockTest_block_impl_0(void *fp, struct __CLBlock__blockTest_block_desc_0 *desc, int _multiplier, int flags=0) : multiplier(_multiplier) {
    impl.isa = &_NSConcreteStackBlock;//isa指针,Block是对象的标志
    impl.Flags = flags;
    impl.FuncPtr = fp;//函数指针
    Desc = desc;
  }
};
/**函数
第一个参数:Block结构体
第二个参数:传入参数
*/
static int __CLBlock__blockTest_block_func_0(struct __CLBlock__blockTest_block_impl_0 *__cself, int num) {
  int multiplier = __cself->multiplier; // bound by copy

        return num * multiplier;
    }

static struct __CLBlock__blockTest_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __CLBlock__blockTest_block_desc_0_DATA = { 0, sizeof(struct __CLBlock__blockTest_block_impl_0)};

static void _I_CLBlock_blockTest(CLBlock * self, SEL _cmd) {
    int multiplier = 6;
    
/*
    int(^BlockTest)(int) = ^int(int num){
        return num * multiplier;
    };
*/
    int(*BlockTest)(int) = ((int (*)(int))&__CLBlock__blockTest_block_impl_0((void *)__CLBlock__blockTest_block_func_0, &__CLBlock__blockTest_block_desc_0_DATA, multiplier));
    /*
BlockTest(2);
*/
    ((int (*)(__block_impl *, int))((__block_impl *)BlockTest)->FuncPtr)((__block_impl *)BlockTest, 2);
}
// @end

struct __block_impl {
  void *isa;//isa指针,Block是对象的标志
  int Flags; //标志变量,在实现block的内部操作时会用到
  int Reserved;//保留变量
  void *FuncPtr;//函数指针
};

通过编译后的源码得知,block编译后为一个含有isa指针的结构体,所以可以将block当做对象;而block的上下文内容被编译后一个函数。而调用时便是将编译后的上下文函数作为参数使用。

static struct __CLBlock__blockTest_block_desc_0 {
  size_t reserved;//保留字段
  size_t Block_size;//block大小(sizeof(struct __main_block_impl_0))
} __CLBlock__blockTest_block_desc_0_DATA = { 0, sizeof(struct __CLBlock__blockTest_block_impl_0)};

在定义__CLBlock__blockTest_block_desc_0结构体时,同时创建了__CLBlock__blockTest_block_desc_0_DATA,并给它赋值,以供在blockTest函数中对__CLBlock__blockTest_block_impl_0进行初始化。

截获变量

为了保证 block 内部能够正常访问外部的变量,block 有一个变量捕获机制。

  • 对于基本数据类型的局部变量截获其值。
  • 对于对象类型的局部变量连同所有权修饰符一起截获。
  • 指针形式截获局部静态变量。
  • 不截获全局变量、静态全局变量
#import "CLBlock.h"
#import "Test.h"

@implementation CLBlock
int global_var = 4;//全局变量
static int static_global_var = 5;//静态全局变量

- (void)blockTest {
    
    //基本数据类型的局部变量
    int var = 6;
    
    //对象类型的局部变量
    __unsafe_unretained id unsafe_objc = nil;
    __strong id strong_obj = nil;
    Test *test = nil;
    
    //静态局部变量
    static int static_var = 7;
    
    void(^BlockTest)(void) = ^{
        NSLog(@"global_var==%d",global_var);
        NSLog(@"static_global_var==%d",static_global_var);
        NSLog(@"var==%d",var);
        NSLog(@"unsafe_objc==%@",unsafe_objc);
        NSLog(@"strong_obj==%@",strong_obj);
        NSLog(@"test==%@",test);
        NSLog(@"static_var==%d",static_var);
    };
    global_var = 41;
    static_global_var = 51;
    var = 61;
    unsafe_objc = [[NSObject alloc] init];
    strong_obj = [[NSObject alloc] init];
    test = [[Test alloc] init];
    static_var = 71;
    
    BlockTest();
}
@end

运行结果

global_var==41
static_global_var==51
var==6
unsafe_objc==(null)
strong_obj==(null)
test==(null)
static_var==71

使用【clang -rewrite-objc -fobjc-arc CLBlock.m】命令进行源码解析

int global_var = 4;
static int static_global_var = 5;


struct __CLBlock__blockTest_block_impl_0 {
  struct __block_impl impl;
  struct __CLBlock__blockTest_block_desc_0* Desc;
    //截获基本数据类型的局部变量的值
  int var;
    //对象类型的局部变量,其值连同所有权修饰符一起截获
  __unsafe_unretained id unsafe_objc;
  __strong id strong_obj;
  Test *__strong test;
    //以指针形式截获静态局部变量
  int *static_var;
    //对全局变量、静态全局变量不截获
    
  __CLBlock__blockTest_block_impl_0(void *fp, struct __CLBlock__blockTest_block_desc_0 *desc, int _var, __unsafe_unretained id _unsafe_objc, __strong id _strong_obj, Test *__strong _test, int *_static_var, int flags=0) : var(_var), unsafe_objc(_unsafe_objc), strong_obj(_strong_obj), test(_test), static_var(_static_var) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __CLBlock__blockTest_block_func_0(struct __CLBlock__blockTest_block_impl_0 *__cself) {
  int var = __cself->var; // bound by copy
  id unsafe_objc = __cself->unsafe_objc; // bound by copy
  id strong_obj = __cself->strong_obj; // bound by copy
  Test *test = __cself->test; // bound by copy
  int *static_var = __cself->static_var; // bound by copy

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_by_brpygpw16r5cz_vx4d7_prcw0000gn_T_CLBlock_a61b60_mi_0,global_var);
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_by_brpygpw16r5cz_vx4d7_prcw0000gn_T_CLBlock_a61b60_mi_1,static_global_var);
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_by_brpygpw16r5cz_vx4d7_prcw0000gn_T_CLBlock_a61b60_mi_2,var);
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_by_brpygpw16r5cz_vx4d7_prcw0000gn_T_CLBlock_a61b60_mi_3,unsafe_objc);
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_by_brpygpw16r5cz_vx4d7_prcw0000gn_T_CLBlock_a61b60_mi_4,strong_obj);
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_by_brpygpw16r5cz_vx4d7_prcw0000gn_T_CLBlock_a61b60_mi_5,test);
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_by_brpygpw16r5cz_vx4d7_prcw0000gn_T_CLBlock_a61b60_mi_6,(*static_var));
    }
static void __CLBlock__blockTest_block_copy_0(struct __CLBlock__blockTest_block_impl_0*dst, struct __CLBlock__blockTest_block_impl_0*src) {_Block_object_assign((void*)&dst->unsafe_objc, (void*)src->unsafe_objc, 3/*BLOCK_FIELD_IS_OBJECT*/);_Block_object_assign((void*)&dst->strong_obj, (void*)src->strong_obj, 3/*BLOCK_FIELD_IS_OBJECT*/);_Block_object_assign((void*)&dst->test, (void*)src->test, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static void __CLBlock__blockTest_block_dispose_0(struct __CLBlock__blockTest_block_impl_0*src) {_Block_object_dispose((void*)src->unsafe_objc, 3/*BLOCK_FIELD_IS_OBJECT*/);_Block_object_dispose((void*)src->strong_obj, 3/*BLOCK_FIELD_IS_OBJECT*/);_Block_object_dispose((void*)src->test, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static struct __CLBlock__blockTest_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __CLBlock__blockTest_block_impl_0*, struct __CLBlock__blockTest_block_impl_0*);
  void (*dispose)(struct __CLBlock__blockTest_block_impl_0*);
} __CLBlock__blockTest_block_desc_0_DATA = { 0, sizeof(struct __CLBlock__blockTest_block_impl_0), __CLBlock__blockTest_block_copy_0, __CLBlock__blockTest_block_dispose_0};

static void _I_CLBlock_blockTest(CLBlock * self, SEL _cmd) {


    int var = 6;


    __attribute__((objc_ownership(none))) id unsafe_objc = __null;
    __attribute__((objc_ownership(strong))) id strong_obj = __null;
    Test *test = __null;


    static int static_var = 7;

    void(*BlockTest)(void) = ((void (*)())&__CLBlock__blockTest_block_impl_0((void *)__CLBlock__blockTest_block_func_0, &__CLBlock__blockTest_block_desc_0_DATA, var, unsafe_objc, strong_obj, test, &static_var, 570425344));
    global_var = 41;
    static_global_var = 51;
    var = 61;
    unsafe_objc = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));
    strong_obj = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));
    test = ((Test *(*)(id, SEL))(void *)objc_msgSend)((id)((Test *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Test"), sel_registerName("alloc")), sel_registerName("init"));
    static_var = 71;

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

__block修饰符

对被截获的局部变量进行赋值操作需要__block修饰符;
对于静态局部变量、全局变量、静态全局变量不需要__block修饰符;

赋值≠使用
    NSMutableArray *mArray = [[NSMutableArray alloc] init];
    void(^Block)(void) = ^{
        [mArray addObject:@123];
    };
    Block();

此种情况不需要__block修饰符。

__block原理

__block修饰的变量变成了对象。

示例:

#import "CLBlock.h"
#import "Test.h"

@implementation CLBlock
int global_var = 4;//全局变量
static int static_global_var = 5;//静态全局变量

- (void)blockTest {
    
    //基本数据类型的局部变量
    __block int var = 6;
    
    //对象类型的局部变量
    __block __unsafe_unretained id unsafe_objc = nil;
    __block __strong id strong_obj = nil;
    __block Test *test = nil;
    
    //静态局部变量
    static int static_var = 7;
    
    void(^BlockTest)(void) = ^{
        NSLog(@"global_var==%d",global_var);
        NSLog(@"static_global_var==%d",static_global_var);
        NSLog(@"var==%d",var);
        NSLog(@"unsafe_objc==%@",unsafe_objc);
        NSLog(@"strong_obj==%@",strong_obj);
        NSLog(@"test==%@",test);
        NSLog(@"static_var==%d",static_var);
    };
    global_var = 41;
    static_global_var = 51;
    var = 61;
    unsafe_objc = [[NSObject alloc] init];
    strong_obj = [[NSObject alloc] init];
    test = [[Test alloc] init];
    static_var = 71;
    
    BlockTest();
    
    
    NSMutableArray *mArray = [[NSMutableArray alloc] init];
    void(^Block)(void) = ^{
        [mArray addObject:@123];
    };
    Block();
}
@end

运行结果:

global_var==41
static_global_var==51
var==61
unsafe_objc==<NSObject: 0x600003ddc940>
strong_obj==<NSObject: 0x600003ddc940>
test==<Test: 0x600003ddc970>
static_var==71

查看源码编译:

int global_var = 4;
static int static_global_var = 5;

struct __Block_byref_var_0 {
  void *__isa;
__Block_byref_var_0 *__forwarding;
 int __flags;
 int __size;
 int var;
};
struct __Block_byref_unsafe_objc_1 {
  void *__isa;
__Block_byref_unsafe_objc_1 *__forwarding;
 int __flags;
 int __size;
 __unsafe_unretained id unsafe_objc;
};
struct __Block_byref_strong_obj_2 {
  void *__isa;
__Block_byref_strong_obj_2 *__forwarding;
 int __flags;
 int __size;
 void (*__Block_byref_id_object_copy)(void*, void*);
 void (*__Block_byref_id_object_dispose)(void*);
 __strong id strong_obj;
};
struct __Block_byref_test_3 {
  void *__isa;
__Block_byref_test_3 *__forwarding;
 int __flags;
 int __size;
 void (*__Block_byref_id_object_copy)(void*, void*);
 void (*__Block_byref_id_object_dispose)(void*);
 Test *__strong test;
};

struct __CLBlock__blockTest_block_impl_0 {
  struct __block_impl impl;
  struct __CLBlock__blockTest_block_desc_0* Desc;
  int *static_var;
  __Block_byref_var_0 *var; // by ref
  __Block_byref_unsafe_objc_1 *unsafe_objc; // by ref
  __Block_byref_strong_obj_2 *strong_obj; // by ref
  __Block_byref_test_3 *test; // by ref
  __CLBlock__blockTest_block_impl_0(void *fp, struct __CLBlock__blockTest_block_desc_0 *desc, int *_static_var, __Block_byref_var_0 *_var, __Block_byref_unsafe_objc_1 *_unsafe_objc, __Block_byref_strong_obj_2 *_strong_obj, __Block_byref_test_3 *_test, int flags=0) : static_var(_static_var), var(_var->__forwarding), unsafe_objc(_unsafe_objc->__forwarding), strong_obj(_strong_obj->__forwarding), test(_test->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
......
此处省略部分代码
......
static void _I_CLBlock_blockTest(CLBlock * self, SEL _cmd) {


    __attribute__((__blocks__(byref))) __Block_byref_var_0 var = {(void*)0,(__Block_byref_var_0 *)&var, 0, sizeof(__Block_byref_var_0), 6};


    __attribute__((__blocks__(byref))) __attribute__((objc_ownership(none))) __Block_byref_unsafe_objc_1 unsafe_objc = {(void*)0,(__Block_byref_unsafe_objc_1 *)&unsafe_objc, 0, sizeof(__Block_byref_unsafe_objc_1), __null};
    __attribute__((__blocks__(byref))) __attribute__((objc_ownership(strong))) __Block_byref_strong_obj_2 strong_obj = {(void*)0,(__Block_byref_strong_obj_2 *)&strong_obj, 33554432, sizeof(__Block_byref_strong_obj_2), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, __null};
    __attribute__((__blocks__(byref))) __Block_byref_test_3 test = {(void*)0,(__Block_byref_test_3 *)&test, 33554432, sizeof(__Block_byref_test_3), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, __null};


    static int static_var = 7;

    void(*BlockTest)(void) = ((void (*)())&__CLBlock__blockTest_block_impl_0((void *)__CLBlock__blockTest_block_func_0, &__CLBlock__blockTest_block_desc_0_DATA, &static_var, (__Block_byref_var_0 *)&var, (__Block_byref_unsafe_objc_1 *)&unsafe_objc, (__Block_byref_strong_obj_2 *)&strong_obj, (__Block_byref_test_3 *)&test, 570425344));
    global_var = 41;
    static_global_var = 51;
    (var.__forwarding->var) = 61;
    (unsafe_objc.__forwarding->unsafe_objc) = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));
    (strong_obj.__forwarding->strong_obj) = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));
    (test.__forwarding->test) = ((Test *(*)(id, SEL))(void *)objc_msgSend)((id)((Test *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Test"), sel_registerName("alloc")), sel_registerName("init"));
    static_var = 71;

    ((void (*)(__block_impl *))((__block_impl *)BlockTest)->FuncPtr)((__block_impl *)BlockTest);


    NSMutableArray *mArray = ((NSMutableArray *(*)(id, SEL))(void *)objc_msgSend)((id)((NSMutableArray *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSMutableArray"), sel_registerName("alloc")), sel_registerName("init"));
    void(*Block)(void) = ((void (*)())&__CLBlock__blockTest_block_impl_1((void *)__CLBlock__blockTest_block_func_1, &__CLBlock__blockTest_block_desc_1_DATA, mArray, 570425344));
    ((void (*)(__block_impl *))((__block_impl *)Block)->FuncPtr)((__block_impl *)Block);
}

以基本数据类型为例,通过与添加__block修饰符之前源码对比发现:
int类型变为__Block_byref_var_0*对象类型;
赋值时由var = 61;变为(var.__forwarding->var) = 61;

__forwarding.png

Block的内存管理

根据Block在内存中的位置分为三种类型:

  • 全局类型:_NSConcreteGlobalBlock,位于全局区的block,它是设置在程序的数据区域(.data区)中。
  • 栈类型:_NSConcreteStackBlock,位于栈区,超出变量作用域,栈上的Block以及 __block变量都被销毁。
  • 堆类型:_NSConcreteMallocBlock位于堆区,在变量作用域结束时不受影响。
_NSConcreteGlobalBlock

生成全局类型Block有两种情况

  • 定义全局变量的地方有Block语法时
#import "CLBlock.h"


@implementation CLBlock

void(^BlockTest)(void) = ^{ };

- (void)blockTest {
   
}
@end

编译后

struct __BlockTest_block_impl_0 {
  struct __block_impl impl;
  struct __BlockTest_block_desc_0* Desc;
  __BlockTest_block_impl_0(void *fp, struct __BlockTest_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteGlobalBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
  • Block语法不使用截获的自动变量时候
    int (^BlockTest)(int num) = ^(int num){
        return num;
    };
    BlockTest(2);

虽然,这个Block在循环内,但是Block的地址总是不变的。说明这个Block在全局段。注:针对没有捕获自动变量的Block来说,虽然用clang的rewrite-objc转化后的代码中仍显示_NSConcretStackBlock,但是实际上不是这样的。????

_NSConcreteStackBlock

设置在栈上的Block,如果其作用域结束,该Block就被销毁。同样的,由于__block变量也配置在栈上,如果其作用域结束,则该__block变量也会被销毁。

_NSConcreteMallocBlock

堆类型Block无法直接创建,需要由_NSConcreteStackBlock类型的block拷贝而来(也就是说block需要执行copy之后才能存放到堆中)。

  • 栈Block被拷贝生成到堆情况

1.调用Block的copy实例方法时
2.Block作为函数返回值返回时
3.将Block赋值给附有__strong修饰符id类型的类或Block类型成员变量时
4.将方法名中含有usingBlock的Cocoa框架方法或GCD的API中传递Block时

Block的copy操作
copy.png
Block由栈 copy至堆的内存使用变化
  • 当栈上的Block被复制到堆上时,__block变量也会随之复制,并且Block持有该变量


    单个Block使用__block变量.png
  • 多个Block中使用__block变量时,任何一个Block被复制到堆时,__block变量也会一并复制到堆并被持有,其余Block被复制时,仅需要__block 变量的引用计数


    多个Block中使用__block变量.png

和使用引用计数一样,在堆上的Block被废弃,它所引用的__block变量将被释放


废弃Block.png

理解Block作用域之后,我们发现这和OC引用计数方式管理方式一样,使用__block修饰符来持有对象,当Block被废弃之后,__block修饰变量也随之释放

当Block被复制到堆之后,会将自身的__forwarding指针更新,依然指向“最新”的自己,这样就保证了在栈上或者堆上都能正确访问对应变量


复制之后.png

__forwarding无论在任何内存位置,都可以顺利的访问同一个__block变量。

Block的循环引用

循环引用

当前对象持有block,在block中使用self又造成block持有self,引起循环引用。

- (void)blockTest {
    
    _block = ^{
        NSLog(@"%@",self.test);
    };
       
}

使用__weak关键字解除循环引用:

- (void)blockTest {
    
    __weak typeof(self)weakSelf = self;
    _block = ^{
        NSLog(@"%@",weakSelf.test);
    };
  
}
__block引起的循环引用

注意:
MRC 下,__block修改对象不会引起其引用计数,避免了循环引用。
ARC下,__block修改对象会引起强引用,无法避免了循环引用,需要手动解环。

- (void)blockTest {
    
    __block CLBlock *blockSelf = self;
    _block = ^{
        NSLog(@"%@",blockSelf.test);
    };
    _block();
}

对象持有Block,Block持有__block变量,__block变量持有对象,引起大环引用。
解决方式:

- (void)blockTest {
    
    __block CLBlock *blockSelf = self;
    _block = ^{
        NSLog(@"%@",blockSelf.test);
        blockSelf = nil;
    };
    _block();
}

注意:如果Block一直不调用,无法解环。

Block对对象的长引用

Block 中如果持有了self就会对其强引用,项目中有许多地方在Block中持有self并不会引起循环引用(如在封装的网络请求中),但是可能持有较长的时间,建议此时使用__weak进行弱引用处理。

    __weak typeof(self)weakSelf = self;
    [[NetworkRequest sharedInstance] request:@{}
                                successBlock:^(id  _Nullable responseObject) {
        weakSelf.responseObject = responseObject;
    } errorBlock:^(NSError *error) {

    } failureBlock:^(NSError *error) {

    }];

__strong使用

如果网络异步返回Block,Block执行的过程中页面返回,由于__weak对对象进行弱引用,则此时对象会被释放,异步运行中的对象变成nil,引起异常。

示例:

    __weak typeof(self)weakSelf = self;
    [[NetworkRequest sharedInstance] request:@{} successBlock:^(id  _Nullable responseObject) {
        NSLog(@"weakSelf对象地址:%@",weakSelf);
        for (int i = 0; i < 10000; i++) {
            NSLog(@"%d",i);
        }
        NSLog(@"耗时的任务 结束 weakSelf对象地址:%@",weakSelf);
    } errorBlock:^(NSError * _Nonnull error) {
        
    } failureBlock:^(NSError * _Nonnull error) {
        
    }];

打印结果

weakSelf对象地址:<AViewController: 0x7fc2ffd06c10>
...
省略
...
耗时的任务 结束 weakSelf对象地址:(null)

此时需要在Block中使用__strong修饰符,在Block开始执行时,检验弱引用的对象是否还存在,如果存在,使用__strong进行强引用,此时引用计数加1,这样在Block执行的过程中,这个对象就不会被置为nil,而在Block执行完毕后,对象的引用计数就会减1,这样就不会导致对象无法释放。

    __weak typeof(self)weakSelf = self;
    [[NetworkRequest sharedInstance] request:@{} successBlock:^(id  _Nullable responseObject) {
        if (!weakSelf) return;
        __strong typeof(weakSelf)strongSelf = weakSelf;
        NSLog(@"strongSelf对象地址:%@",strongSelf);
        for (int i = 0; i < 10000; i++) {
            // 模拟一个耗时的任务
            NSLog(@"%d",i);
        }
        NSLog(@"耗时的任务 结束 strongSelf对象地址:%@",strongSelf);
    } errorBlock:^(NSError * _Nonnull error) {
        
    } failureBlock:^(NSError * _Nonnull error) {
        
    }];

运行结果:

strongSelf对象地址:<AViewController: 0x7f9f5371ee60>
...
省略
...
耗时的任务 结束 strongSelf对象地址:<AViewController: 0x7f9f5371ee60>

区别:
直接使用self,会在编译时强引用self,引起循环引用;
使用__weak后再在Block中使用__strong,则只会在运行到Block中时才会强引用,引用计数进行加1操作,Block执行完毕后引用计数减1。


  • 参考及内容引用资料

iOS中Block的用法,举例,解析与底层原理(这可能是最详细的Block解析)
(四)Block之 __block修饰符及其存储域
iOS中Block实现原理的全面分析
__weak和__strong在Block中的使用
Block的本质与使用

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,002评论 6 509
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,777评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,341评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,085评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,110评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,868评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,528评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,422评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,938评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,067评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,199评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,877评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,540评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,079评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,192评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,514评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,190评论 2 357