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==
strong_obj==
test==
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对象地址:
...
省略
...
耗时的任务 结束 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对象地址:
...
省略
...
耗时的任务 结束 strongSelf对象地址:

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


  • 参考及内容引用资料

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

你可能感兴趣的:(iOS Block简介)