iOS Block简介

作者: FieryDragon | 来源:发表于2020-08-22 18:11 被阅读0次

    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的本质与使用

    相关文章

      网友评论

        本文标题:iOS Block简介

        本文链接:https://www.haomeiwen.com/subject/hivikktx.html