美文网首页
Block 知识点总结

Block 知识点总结

作者: 小木头 | 来源:发表于2018-12-27 20:44 被阅读5次

    面试中经常会被问到什么是 Block, 谈谈你对 Block 的理解, 今天就做一个小的总结。

    Block 定义

    block 是将函数及其上下文封装起来的对象

    首先, 定义一个简单的 AddBlock

      - (void)test1 {
        
        void(^AddBlock)(NSInteger a , NSInteger b) = ^(NSInteger a, NSInteger b) {
            NSLog(@"%ld",a+b);
        };
        AddBlock(3,4);
    }
    

    接着运行相关命令转换成 C++ 文件

    clang -rewrite-objc TestBlock.m
    

    __block_impl

    struct __block_impl {
      void *isa;  // isa指针, 验证了 block 也是一个对象
      int Flags;
      int Reserved;
      void *FuncPtr; // 执行调用的函数指针
    };
    

    具体调用流程

      struct __TestBlock__test1_block_impl_0 {
      struct __block_impl impl;
      struct __TestBlock__test1_block_desc_0* Desc;
      __TestBlock__test1_block_impl_0(void *fp, struct __TestBlock__test1_block_desc_0 *desc, int flags=0) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    static void __TestBlock__test1_block_func_0(struct __TestBlock__test1_block_impl_0 *__cself, NSInteger a, NSInteger b) {
    
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_f7_1v6qx94n2gl27sx0j1dqlrs40000gn_T_TestBlock_54a84e_mi_0,a+b);
        }
    
    static struct __TestBlock__test1_block_desc_0 {
      size_t reserved;
      size_t Block_size;
    } __TestBlock__test1_block_desc_0_DATA = { 0, sizeof(struct __TestBlock__test1_block_impl_0)};
    
    static void _I_TestBlock_test1(TestBlock * self, SEL _cmd) {
    
        void(*AddBlock)(NSInteger a , NSInteger b) = ((void (*)(NSInteger, NSInteger))&__TestBlock__test1_block_impl_0((void *)__TestBlock__test1_block_func_0, &__TestBlock__test1_block_desc_0_DATA));
        ((void (*)(__block_impl *, NSInteger, NSInteger))((__block_impl *)AddBlock)->FuncPtr)((__block_impl *)AddBlock, 3, 4);
    }
    

    最终会执行 __TestBlock__test1_block_impl_0 方法, 并把 isa 指针指向了 _NSConcreteStackBlock, desc 包含了 block 结构体大小等信息

    截获变量

    定义基本类型和对象相关的全局变量、静态全局变量、局部变量,局部静态变量

    #import "TestBlock.h"
    
    
    static int static_global_a; // 基本类型静态全局变量
    int global_a; // 基本类型全局变量
    
    static NSObject *static_global_object; // 静态全局对象
    NSObject *global_object;    // 全局对象
    
    @implementation TestBlock
    
    
    - (void)test2 {
        static int static_inside_a; // 基本类型局部静态变量
        int inside_a; // 基本类型局部变量
        static NSObject *static_inside_object;// 静态局部对象
        NSObject *inside_object; // 局部对象
        
        static_global_a = 1;
        global_a = 2;
        static_inside_a = 3;
        inside_a = 4;
        
        static_global_object = [[NSObject alloc] init];
        global_object = [[NSObject alloc] init];
        static_inside_object = [[NSObject alloc] init];
        inside_object = [[NSObject alloc] init];
        
        void(^Block)(void) = ^(){
            NSLog(@"截获基本数据类型 %d,%d,%d,%d",static_global_a,global_a ,static_inside_a,inside_a);
            
            NSLog(@"截获对象 %@,%@,%@,%@",static_global_object,global_object ,static_inside_object,inside_object);
        };
        
        Block();
    }
    @end
    

    转换后的实现如下

    struct __TestBlock__test2_block_impl_0 {
      struct __block_impl impl;
      struct __TestBlock__test2_block_desc_0* Desc;
      int *static_inside_a;
      int inside_a;
      NSObject **static_inside_object;
      NSObject *inside_object;
      __TestBlock__test2_block_impl_0(void *fp, struct __TestBlock__test2_block_desc_0 *desc, 
                                      int *_static_inside_a, int _inside_a, 
                                      NSObject **_static_inside_object, NSObject *_inside_object, int flags=0) : static_inside_a(_static_inside_a), inside_a(_inside_a), static_inside_object(_static_inside_object), inside_object(_inside_object) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    

    可以发现 Block 只截获了 基本类型和对象的局部变量, 不同点是静态变量是以指针形式存在的, 并没有截获全局变量、静态全局变量, 对象类型的局部变量连同修饰权一起截获

    类型 局部 静态局部 全局 静态全局
    基本类型 ✔️ ✔️ ✖️ ✖️
    对象 ✔️ ✔️ ✖️ ✖️

    __block 修饰符

    用简单的话来说, 如果要对截获的变量进行赋值操作, 需要添加 __block 修饰符

    截获变量.png

    用下面的例子看一下 __block 的具体实现

      - (void)test3 {
        __block int b = 2;
        void(^Block)(void) = ^(){
            b = 4;                  // __block 修饰后可以直接赋值
            NSLog(@"%d",b);
        };
        Block();
    }
    

    __block 修饰的对象转变成结构体 __Block_byref_b_0

      struct __Block_byref_b_0 {
      void *__isa;
    __Block_byref_b_0 *__forwarding;
     int __flags;
     int __size;
     int b;
    };
    
    struct __TestBlock__test3_block_impl_0 {
      struct __block_impl impl;
      struct __TestBlock__test3_block_desc_0* Desc;
      __Block_byref_b_0 *b; // by ref
      __TestBlock__test3_block_impl_0(void *fp, struct __TestBlock__test3_block_desc_0 *desc, __Block_byref_b_0 *_b, int flags=0) : b(_b->__forwarding) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    
    static void __TestBlock__test3_block_func_0(struct __TestBlock__test3_block_impl_0 *__cself) {
      __Block_byref_b_0 *b = __cself->b; // bound by ref
    
            (b->__forwarding->b) = 4;
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_f7_1v6qx94n2gl27sx0j1dqlrs40000gn_T_TestBlock_b681a6_mi_2,(b->__forwarding->b));
        }
    

    可以看到 __block 修饰的对象 b 重新赋值后, 底层转变为 b->__forwarding->b

    __forwarding 作用

    wa

    __forwarding 存在的意义就是不论在任何位置, 都能够顺利访问同一个 __block 变量

    Block 类型

    • _NSConcreteStackBlock
    • _NSConcreateGlobalBlock
    • _NSConcreateMallocBlock

    MRC 情况下:

    • _NSConcreateGlobalBlock:未截获外部变量的情况
    • _NSConcreteStackBlock:引用外部变量,
    • _NSConcreateMallocBlock : [block copy] 或者截获__block 修饰的变量

    ARC 情况下

    • _NSConcreateGlobalBlock : 未截获外部变量的情况
    • _NSConcreateMallocBlock : 变量在赋值的时候自动拷贝到堆区

    内存管理

    Block 内存管理.png

    MRC 下, 栈上的 Block 随着作用域结束会被销毁, 堆上的 Block 随着作用域结束不会被释放

    相关文章

      网友评论

          本文标题:Block 知识点总结

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