美文网首页
Block 原理浅析

Block 原理浅析

作者: 紫藤花的魂 | 来源:发表于2021-08-23 10:30 被阅读0次

    Block 浅析

    一、Block内存

    (堆、栈、全局)

    • 知识点:
    1. 区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。
    2. 区(malloc) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收。
    3. 全局区(静态区)(global)—,全局变量和静态变量的存储是放在一块的,初始化的 全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后由系统释放。
    Block定义:

    Block一个里面存储了指向定义block时的代码块的函数指针,以及block外部上下文变量信息的结构体

    OC 中有三种常见的Block:全局区、栈区、堆区。
    1. 全局Block
      没有访问外部局部变量(基本数据、OC对象)、成员属性变量 或 只访问全局变量、静态变量(全局静态或局部静态)
    //全局静态变量
    int static globalStaticInt = 100; //全局变量
    //全局变量
    int globalInt = 100;
    
    - (void)globalBlock{
        //全局Block
        //局部静态变量
        int static tempStaticInt = 10;
        
        void(^testBlock)(int a) = ^(int a){
            NSLog(@"%d",tempStaticInt);
        };
        //runtime 获取block的类
        Class blockClass = object_getClass(testBlock);
        NSLog(@"testBlock :%@",blockClass);
        //runtime 获取类的父类
        Class superClass = class_getSuperclass(blockClass);
        while (superClass) {
             NSLog(@"superClass:%@",superClass);
             superClass = class_getSuperclass(superClass);
        }
        
        testBlock(4);
    }
    

    Block的继承关系:__NSGlobalBlock__ -----> NSBlock -----> NSObject

    1. 栈区Block
    • MRC:用到了外部局部变量(基本数据、OC对象)、成员属性变量,且没有强引用Block。// 文件添加 -fno-objc-arc 标记变为MRC编译。
    • MRC:用到了外部局部变量(基本数据、OC对象)、成员属性变量,且Block用weak修饰。
    //局部变量
        int temInt = 100;
        UILabel *templabel = [UILabel new];
        //成员变量
        memberLabel = [UILabel new];
        //属性
        self.propertyLabel =  [UILabel new];
        
        void(^tempBlock)(int a) = ^(int a){
            NSLog(@"%d",temInt);
            NSLog(@"%@",templabel);
            NSLog(@"%@",memberLabel);
            NSLog(@"%@",self.propertyLabel);
        };
        
        Class blockClass = object_getClass(tempBlock);
        NSLog(@"testBlock :%@",blockClass);
        Class superClass = class_getSuperclass(blockClass);
        while (superClass) {
             NSLog(@"superClass:%@",superClass);
             superClass = class_getSuperclass(superClass);
        }
        
        tempBlock(4);
    

    Block的继承关系:__NSStackBlock__ -----> NSBlock -----> NSObject

    1. 堆区Block
    • 用到了外部局部变量(基本数据、OC对象)、成员属性变量,且有强引用Block。
    • ARC: 用到了外部局部变量(基本数据、OC对象)、成员属性变量,且没有强引用Block,创建之初是栈区Block,创建后会执行_Block_copy函数,将栈区Block复制在堆区。
        int tempInt = 100;
        self.strongBlock = ^{
    //        NSLog(@"%@",self.propertyLabel);
            NSLog(@"%d",globalStaticInt);
            NSLog(@"%d",tempInt);
        };
        
        self.copyBlock = ^{
    //        NSLog(@"%@",self.propertyLabel);
            NSLog(@"%d",globalStaticInt);
            NSLog(@"%d",tempInt);
        };
        
        self.weakBlock = ^{
    //        NSLog(@"%@",self.propertyLabel);
            NSLog(@"%d",globalStaticInt);
            NSLog(@"%d",tempInt);
        };
        
        Class strongBlockClass = object_getClass(self.strongBlock);
        Class copyBlockClass = object_getClass(self.copyBlock);
        Class weakBlockClass = object_getClass(self.weakBlock);
        
        NSLog(@"strongBlockClass:%@ *** copyBlockClass:%@  *** weakBlockClass:%@",strongBlockClass,copyBlockClass,weakBlockClass);
    

    Block(strong,copy)的继承关系:__NSMallocBlock__ -----> NSBlock -----> NSObject

    • 灵魂拷问:block在内存中的哪个区与block有没有参数是否有关系呢?如果有,那个于参数种类又有没有关系呢?

    二、Block结构

    (oc的Block编译后长什么样子?)

    1. 首先来看几个block例子:
    • 截获局部变量的Block
            int age = 10;
            void (^block)(void) = ^() {
                NSLog(@"age = %d,",age);
            };
    
            age = 20;
            block();
    
    • 截获静态局部变量的Block
            int static age = 10;
            void (^block)(void) = ^() {
                NSLog(@"age = %d,",age);
            };
    
            age = 20;
            block();
    
    • 截获全局变量的Block
            int static age = 10;
            void (^block)(void) = ^() {
                NSLog(@"age = %d,",age);
            };
    
            age = 20;
            block();
    
    • 上面三个block执行后最终输出的age的值是啥呢?为什么会这样?
    1. Block转为C++
      显示SDK版本:xcodebuild -showsdks
      转成C++:
      xcrun -sdk iphonesimulator14.5 clang -S -rewrite-objc -fobjc-arc -fobjc-runtime=ios-14.5 VC.m

    block的基础信息的结构体(所有的Block都是以此为结构为模板)

    struct __block_impl {
      void *isa;   //无类型指针
      int Flags;   //标志
      int Reserved; //保留值
      void *FuncPtr;  //函数指针
    };
    
    • 2.1空Block
    //初始化
    void(^voidBlock) (void) = ^ {
        NSLog(@"空Block");
    };
    voidBlock(); //执行
    

    转成C++代码

    //voidBlock 对应的结构体
    struct __VC__voidBlock_block_impl_0 {
      struct __block_impl impl;
      struct __VC__voidBlock_block_desc_0* Desc;  //描述信息
    //构造函数
      __VC__voidBlock_block_impl_0(void *fp, struct __VC__voidBlock_block_desc_0 *desc, int flags=0) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;    //flags标记
        impl.FuncPtr = fp;     //block块内执行的代码的函数指针
        Desc = desc;           //描述信息
      }
    };
    //block块内执行的代码的函数
    static void __VC__voidBlock_block_func_0(struct __VC__voidBlock_block_impl_0 *__cself) {
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_pr_3v7lmlh17sd92x_qktr9b2w00000gn_T_VC_4722ae_mi_0);
        }
    //描述信息
    static struct __VC__voidBlock_block_desc_0 {
      size_t reserved;      //保留位
      size_t Block_size;  //block的大小
    } __VC__voidBlock_block_desc_0_DATA = { 0, sizeof(struct __VC__voidBlock_block_impl_0)};  //实例化
    
    //voidBlock 初始化
        void(*voidBlock) (void) = ((void (*)())&__VC__voidBlock_block_impl_0((void *)__VC__voidBlock_block_func_0, &__VC__voidBlock_block_desc_0_DATA));
    //获取voidBlock对应的结构体的FuncPtr函数指针,并执行。voidBlock();
        ((void (*)(__block_impl *))((__block_impl *)voidBlock)->FuncPtr)((__block_impl *)voidBlock);
    }
    
    • 2.2临时变量Block,定义结构体时候添加了对应的变量
    int tempInt = 100;
    //初始化
        void(^simpleDateBlock) (void) = ^ {
            NSLog(@"简单实数Block -- %d",tempInt);
        };
        simpleDateBlock();  //执行
    

    转C++

    struct __VC__simpleDateBlock_block_impl_0 {
      struct __block_impl impl;
      struct __VC__simpleDateBlock_block_desc_0* Desc;
      int tempInt;
      __VC__simpleDateBlock_block_impl_0(void *fp, struct __VC__simpleDateBlock_block_desc_0 *desc, int _tempInt, int flags=0) : tempInt(_tempInt) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    static void __VC__simpleDateBlock_block_func_0(struct __VC__simpleDateBlock_block_impl_0 *__cself) {
      int tempInt = __cself->tempInt; // bound by copy
    
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_pr_3v7lmlh17sd92x_qktr9b2w00000gn_T_VC_4722ae_mi_1,tempInt);
        }
    
    static struct __VC__simpleDateBlock_block_desc_0 {
      size_t reserved;
      size_t Block_size;
    } __VC__simpleDateBlock_block_desc_0_DATA = { 0, sizeof(struct __VC__simpleDateBlock_block_impl_0)};
    
    • 2.3包含临时的objc对象的block:定义结构体时候添加了对应__strong类型的变量
    UILabel *lab = [UILabel new];
        void(^tempObjDateBlock) (void) = ^ {
            NSLog(@"简单实数Block -- %@",lab);
        };
        tempObjDateBlock();
    

    转C++

    struct __VC__tempObjDateBlock_block_impl_0 {
      struct __block_impl impl;
      struct __VC__tempObjDateBlock_block_desc_0* Desc;
      UILabel *__strong lab;
      __VC__tempObjDateBlock_block_impl_0(void *fp, struct __VC__tempObjDateBlock_block_desc_0 *desc, UILabel *__strong _lab, int flags=0) : lab(_lab) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    static void __VC__tempObjDateBlock_block_func_0(struct __VC__tempObjDateBlock_block_impl_0 *__cself) {
      UILabel *__strong lab = __cself->lab; // bound by copy
    
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_pr_3v7lmlh17sd92x_qktr9b2w00000gn_T_VC_4722ae_mi_2,lab);
        }
    static void __VC__tempObjDateBlock_block_copy_0(struct __VC__tempObjDateBlock_block_impl_0*dst, struct __VC__tempObjDateBlock_block_impl_0*src) {_Block_object_assign((void*)&dst->lab, (void*)src->lab, 3/*BLOCK_FIELD_IS_OBJECT*/);}
    
    static void __VC__tempObjDateBlock_block_dispose_0(struct __VC__tempObjDateBlock_block_impl_0*src) {_Block_object_dispose((void*)src->lab, 3/*BLOCK_FIELD_IS_OBJECT*/);}
    
    static struct __VC__tempObjDateBlock_block_desc_0 {
      size_t reserved;
      size_t Block_size;
      void (*copy)(struct __VC__tempObjDateBlock_block_impl_0*, struct __VC__tempObjDateBlock_block_impl_0*);
      void (*dispose)(struct __VC__tempObjDateBlock_block_impl_0*);
    } __VC__tempObjDateBlock_block_desc_0_DATA = { 0, sizeof(struct __VC__tempObjDateBlock_block_impl_0), __VC__tempObjDateBlock_block_copy_0, __VC__tempObjDateBlock_block_dispose_0};
    
    • 2.4成员变量(objc)的block:强引入是VC对应的self,通过self.label获取值
    _label = [UILabel new];
        void(^tempMemObjDateBlock) (void) = ^ {
            NSLog(@"简单实数Block -- %@",_label);
        };
        tempMemObjDateBlock();
    

    转C++

    struct __VC__tempMemObjDateBlock_block_impl_0 {
      struct __block_impl impl;
      struct __VC__tempMemObjDateBlock_block_desc_0* Desc;
      VC *const __strong self;  
      __VC__tempMemObjDateBlock_block_impl_0(void *fp, struct __VC__tempMemObjDateBlock_block_desc_0 *desc, VC *const __strong _self, int flags=0) : self(_self) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    static void __VC__tempMemObjDateBlock_block_func_0(struct __VC__tempMemObjDateBlock_block_impl_0 *__cself) {
      VC *const __strong self = __cself->self; // bound by copy
    
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_pr_3v7lmlh17sd92x_qktr9b2w00000gn_T_VC_4722ae_mi_3,(*(UILabel *__strong *)((char *)self + OBJC_IVAR_$_VC$_label)));
        }
    static void __VC__tempMemObjDateBlock_block_copy_0(struct __VC__tempMemObjDateBlock_block_impl_0*dst, struct __VC__tempMemObjDateBlock_block_impl_0*src) {_Block_object_assign((void*)&dst->self, (void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);}
    
    static void __VC__tempMemObjDateBlock_block_dispose_0(struct __VC__tempMemObjDateBlock_block_impl_0*src) {_Block_object_dispose((void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);}
    
    static struct __VC__tempMemObjDateBlock_block_desc_0 {
      size_t reserved;
      size_t Block_size;
      void (*copy)(struct __VC__tempMemObjDateBlock_block_impl_0*, struct __VC__tempMemObjDateBlock_block_impl_0*);
      void (*dispose)(struct __VC__tempMemObjDateBlock_block_impl_0*);
    } __VC__tempMemObjDateBlock_block_desc_0_DATA = { 0, sizeof(struct __VC__tempMemObjDateBlock_block_impl_0), __VC__tempMemObjDateBlock_block_copy_0, __VC__tempMemObjDateBlock_block_dispose_0};
    
    //初始化
    (*(UILabel *__strong *)((char *)self + OBJC_IVAR_$_VC$_label)) = ((UILabel *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("UILabel"), sel_registerName("new"));
    //初始化
        void(*tempMemObjDateBlock) (void) = ((void (*)())&__VC__tempMemObjDateBlock_block_impl_0((void *)__VC__tempMemObjDateBlock_block_func_0, &__VC__tempMemObjDateBlock_block_desc_0_DATA, self, 570425344));
    //执行
        ((void (*)(__block_impl *))((__block_impl *)tempMemObjDateBlock)->FuncPtr)((__block_impl *)tempMemObjDateBlock);
    
    • 2.5全局变量global value:未添加变量,而是直接使用全局区globalV
    int globalV = 100;
    - (void)globalValueBlock {
        void(^globalValueBlock) (void) = ^ {
            NSLog(@"简单实数Block -- %d",globalV);
        };
        globalValueBlock();
    }
    

    转C++:

    struct __VC__globalValueBlock_block_impl_0 {
      struct __block_impl impl;
      struct __VC__globalValueBlock_block_desc_0* Desc;
      __VC__globalValueBlock_block_impl_0(void *fp, struct __VC__globalValueBlock_block_desc_0 *desc, int flags=0) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    static void __VC__globalValueBlock_block_func_0(struct __VC__globalValueBlock_block_impl_0 *__cself) {
    
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_pr_3v7lmlh17sd92x_qktr9b2w00000gn_T_VC_4722ae_mi_4,globalV);
        }
    
    static struct __VC__globalValueBlock_block_desc_0 {
      size_t reserved;
      size_t Block_size;
    } __VC__globalValueBlock_block_desc_0_DATA = { 0, sizeof(struct __VC__globalValueBlock_block_impl_0)};
    
    static void _I_VC_globalValueBlock(VC * self, SEL _cmd) {
        void(*globalValueBlock) (void) = ((void (*)())&__VC__globalValueBlock_block_impl_0((void *)__VC__globalValueBlock_block_func_0, &__VC__globalValueBlock_block_desc_0_DATA));
        ((void (*)(__block_impl *))((__block_impl *)globalValueBlock)->FuncPtr)((__block_impl *)globalValueBlock);
    }
    
    • 2.6全局静态变量globalStaticV:未添加变量,而是直接使用全局区globalV
    int static globalStaticV = 100;
    //全局变量global value
    - (void)globalStaticValueBlock {
        void(^globalStaticValueBlock) (void) = ^ {
            NSLog(@"简单实数Block -- %d",globalStaticV);
        };
        globalStaticValueBlock();
    }
    

    转C++

    int static globalStaticV = 100;
    struct __VC__globalStaticValueBlock_block_impl_0 {
      struct __block_impl impl;
      struct __VC__globalStaticValueBlock_block_desc_0* Desc;
      __VC__globalStaticValueBlock_block_impl_0(void *fp, struct __VC__globalStaticValueBlock_block_desc_0 *desc, int flags=0) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    static void __VC__globalStaticValueBlock_block_func_0(struct __VC__globalStaticValueBlock_block_impl_0 *__cself) {
    
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_pr_3v7lmlh17sd92x_qktr9b2w00000gn_T_VC_4722ae_mi_5,globalStaticV);
        }
    
    static struct __VC__globalStaticValueBlock_block_desc_0 {
      size_t reserved;
      size_t Block_size;
    } __VC__globalStaticValueBlock_block_desc_0_DATA = { 0, sizeof(struct __VC__globalStaticValueBlock_block_impl_0)};
    
    static void _I_VC_globalStaticValueBlock(VC * self, SEL _cmd) {
        void(*globalStaticValueBlock) (void) = ((void (*)())&__VC__globalStaticValueBlock_block_impl_0((void *)__VC__globalStaticValueBlock_block_func_0, &__VC__globalStaticValueBlock_block_desc_0_DATA));
        ((void (*)(__block_impl *))((__block_impl *)globalStaticValueBlock)->FuncPtr)((__block_impl *)globalStaticValueBlock);
    }
    
    • 2.7局部static value:添加指针变量
    - (void)tempStaticValueBlock {
        int static tempStaticV = 100;
        void(^tempStaticValueBlock) (void) = ^ {
            NSLog(@"简单实数Block -- %d",tempStaticV);
        };
        tempStaticValueBlock();
    }
    

    转C++

    struct __VC__tempStaticValueBlock_block_impl_0 {
      struct __block_impl impl;
      struct __VC__tempStaticValueBlock_block_desc_0* Desc;
      int *tempStaticV;
      __VC__tempStaticValueBlock_block_impl_0(void *fp, struct __VC__tempStaticValueBlock_block_desc_0 *desc, int *_tempStaticV, int flags=0) : tempStaticV(_tempStaticV) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    static void __VC__tempStaticValueBlock_block_func_0(struct __VC__tempStaticValueBlock_block_impl_0 *__cself) {
      int *tempStaticV = __cself->tempStaticV; // bound by copy
    
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_pr_3v7lmlh17sd92x_qktr9b2w00000gn_T_VC_4722ae_mi_6,(*tempStaticV));
        }
    
    static struct __VC__tempStaticValueBlock_block_desc_0 {
      size_t reserved;
      size_t Block_size;
    } __VC__tempStaticValueBlock_block_desc_0_DATA = { 0, sizeof(struct __VC__tempStaticValueBlock_block_impl_0)};
    
    static void _I_VC_tempStaticValueBlock(VC * self, SEL _cmd) {
        int static tempStaticV = 100;
        void(*tempStaticValueBlock) (void) = ((void (*)())&__VC__tempStaticValueBlock_block_impl_0((void *)__VC__tempStaticValueBlock_block_func_0, &__VC__tempStaticValueBlock_block_desc_0_DATA, &tempStaticV));
        ((void (*)(__block_impl *))((__block_impl *)tempStaticValueBlock)->FuncPtr)((__block_impl *)tempStaticValueBlock);
    }
    
    • 2.8局部__block 修饰的变量:新增一个变量对应的结构体,通过结构体的__forwarding->blockV 获取值;新增__VC__blockValueBlock_block_copy_0(_Block_object_assign)、__VC__blockValueBlock_block_dispose_0(_Block_object_dispose)三个函数
    - (void)blockValueBlock {
        __block int blockV = 100;
        void(^blockValueBlock) (void) = ^ {
            NSLog(@"简单实数Block -- %d",blockV);
        };
        blockValueBlock();
    }
    

    转C++

    struct __Block_byref_blockV_0 {
      void *__isa;
    __Block_byref_blockV_0 *__forwarding;
     int __flags;
     int __size;
     int blockV;
    };
    
    struct __VC__blockValueBlock_block_impl_0 {
      struct __block_impl impl;
      struct __VC__blockValueBlock_block_desc_0* Desc;
      __Block_byref_blockV_0 *blockV; // by ref
      __VC__blockValueBlock_block_impl_0(void *fp, struct __VC__blockValueBlock_block_desc_0 *desc, __Block_byref_blockV_0 *_blockV, int flags=0) : blockV(_blockV->__forwarding) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    static void __VC__blockValueBlock_block_func_0(struct __VC__blockValueBlock_block_impl_0 *__cself) {
      __Block_byref_blockV_0 *blockV = __cself->blockV; // bound by ref
    
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_pr_3v7lmlh17sd92x_qktr9b2w00000gn_T_VC_29ca65_mi_7,(blockV->__forwarding->blockV));
        }
    static void __VC__blockValueBlock_block_copy_0(struct __VC__blockValueBlock_block_impl_0*dst, struct __VC__blockValueBlock_block_impl_0*src) {_Block_object_assign((void*)&dst->blockV, (void*)src->blockV, 8/*BLOCK_FIELD_IS_BYREF*/);}
    
    static void __VC__blockValueBlock_block_dispose_0(struct __VC__blockValueBlock_block_impl_0*src) {_Block_object_dispose((void*)src->blockV, 8/*BLOCK_FIELD_IS_BYREF*/);}
    
    static struct __VC__blockValueBlock_block_desc_0 {
      size_t reserved;
      size_t Block_size;
      void (*copy)(struct __VC__blockValueBlock_block_impl_0*, struct __VC__blockValueBlock_block_impl_0*);
      void (*dispose)(struct __VC__blockValueBlock_block_impl_0*);
    } __VC__blockValueBlock_block_desc_0_DATA = { 0, sizeof(struct __VC__blockValueBlock_block_impl_0), __VC__blockValueBlock_block_copy_0, __VC__blockValueBlock_block_dispose_0};
    
    static void _I_VC_blockValueBlock(VC * self, SEL _cmd) {
        __attribute__((__blocks__(byref))) __Block_byref_blockV_0 blockV = {(void*)0,(__Block_byref_blockV_0 *)&blockV, 0, sizeof(__Block_byref_blockV_0), 100};
        void(*blockValueBlock) (void) = ((void (*)())&__VC__blockValueBlock_block_impl_0((void *)__VC__blockValueBlock_block_func_0, &__VC__blockValueBlock_block_desc_0_DATA, (__Block_byref_blockV_0 *)&blockV, 570425344));
        ((void (*)(__block_impl *))((__block_impl *)blockValueBlock)->FuncPtr)((__block_impl *)blockValueBlock);
    }
    
    • 2.9VC对block强引用(block是VC的属性时)其结构没有什么变化

    三、Block源码浅析

    (源码的浅析)
    block源码地址
    https://opensource.apple.com/source/libclosure/libclosure-79/
    因此可以按照这个格式转换即可
    https://opensource.apple.com/tarballs/libclosure/libclosure-79.tar.gz

    1. 首先来看
    __block int a = 0;
    void (^block) (void) = ^ {
        NSLog(@"%d",a);
    };
    NSLog(@"%@",block);
    

    这个block在是堆区还是在栈区?

    在ARC下 NSMallocBlock

    在MRC下 NSStackBlock

    MRC下 那么栈区的block是怎么变成的堆区的block的呢?

    实际上是执行_Block_copy 后栈区的block就变成堆区的block

    _Block_copy源码

    void *_Block_copy(const void *arg) {
        struct Block_layout *aBlock; //申明一个block
    
        if (!arg) return NULL;      //如果源block(arg)为null直接返回
        
        // The following would be better done as a switch statement
        aBlock = (struct Block_layout *)arg;
        if (aBlock->flags & BLOCK_NEEDS_FREE) {//需要释放,在堆区
            // latches on high
            latching_incr_int(&aBlock->flags);//堆区的block 执行copy操作,引用计数加1
            return aBlock;
        }
        else if (aBlock->flags & BLOCK_IS_GLOBAL) {//在全局区
            return aBlock; //全局区,保证全局唯一直接返回
        }
        else { //在栈局区
            // Its a stack block.  Make a copy.
            size_t size = Block_size(aBlock); //获取结构体大小
            struct Block_layout *result = (struct Block_layout *)malloc(size); //在堆区开辟对应的空间大小
            if (!result) return NULL;
            //复制 aBlock 所指的内存内容前 size 个字节到 result 所指的地址上
            memmove(result, aBlock, size); // bitcopy first  
    #if __has_feature(ptrauth_calls)
            // Resign the invoke pointer as it uses address authentication.
            result->invoke = aBlock->invoke;
    
            backtrace_symbols_fd(&result->invoke,1,1); //打印函数指针的对应的函数名称
            
    #if __has_feature(ptrauth_signed_block_descriptors)
            if (aBlock->flags & BLOCK_SMALL_DESCRIPTOR) {
                uintptr_t oldDesc = ptrauth_blend_discriminator(
                        &aBlock->descriptor,
                        _Block_descriptor_ptrauth_discriminator);
                uintptr_t newDesc = ptrauth_blend_discriminator(
                        &result->descriptor,
                        _Block_descriptor_ptrauth_discriminator);
    
                result->descriptor =
                        ptrauth_auth_and_resign(aBlock->descriptor,
                                                ptrauth_key_asda, oldDesc,
                                                ptrauth_key_asda, newDesc);
            }
    #endif
    #endif
    //更新标志位。第一行确保引用计数为0。注释表明这行其实不需要——大概这个时候引用计数已经是0了。我猜保留这行是因为以前有个bug导致这里的引用计数不是0(所以说runtime的代码也会偷懒)。下一行设置了BLOCK_NEEDS_FREE标志位,表明这是一个堆block,一旦引用计数减为0,它所占用的内存将被释放。|1操作设置block的引用计数为1。
            // reset refcount 重新设置引用计数
            result->flags &= ~(BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING);    // XXX not needed
            result->flags |= BLOCK_NEEDS_FREE | 2;  // logical refcount 1
            //这个是把剩下的需要复制到堆区的复制过来,例如__block 修饰的变量或block内使用的外部对象
            _Block_call_copy_helper(result, aBlock); 
            // Set isa last so memory analysis tools see a fully-initialized object.
            //block的isa指针被设置为_NSConcreteMallocBlock,说明这是个堆block。
            result->isa = _NSConcreteMallocBlock;
            return result;
        }
    }
    

    从源码中可以看到if和else if的条件判断分别是引用计数(需要释放)和全局block的判断,如果是都直接返回block出去。else的条件判断就是栈block做copy操作。通过aBlock开启一个新的result空间,并且将aBlock的内容等平移到result中去。这时候就实现了由栈变成堆。

    1. 一个完整的执行流程
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            __block int a = 0;
            void (^block) (void) = ^ {
                NSLog(@"%d",a);
            };
            NSLog(@"%@",block);
            block();
        }
        return 0;
    }
    

    通过clang -rewrite-objc main.m 会生成一个mian.cpp的文件

    __block int a = 0; 变量a对应的结构体

    struct __Block_byref_a_0 {
      void *__isa;
    __Block_byref_a_0 *__forwarding;
     int __flags;
     int __size;
     int a;
    };
    

    void (^block) (void) 对应的Block结构体

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

    NSLog(@"%d",a); 这句打印代码对应的函数,通过传入的__main_block_impl_0的实例__cself,__cself->a获取到a的值并打印

    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
      __Block_byref_a_0 *a = __cself->a; // bound by ref
    
                NSLog((NSString *)&__NSConstantStringImpl__var_folders_pr_3v7lmlh17sd92x_qktr9b2w00000gn_T_main_6fbe70_mi_0,(a->__forwarding->a));
            }
    

    __main_block_copy_0函数是src的block结构体或者是src的block结构体持有的变量__Block_byref_a_0 复制到堆区

    static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);}
    

    Block执行完后释放

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

    Block结构体的附加描述信息

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

    Block结构体的附加描述信息实例化

    int main(int argc, const char * argv[]) {
        /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
    //__Block_byref_a_0 对应的变量a初始化:__block int a = 100;
            __attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 100};
    //block初始化
            void(*voidBlock) (void) =((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344));
    //block执行
            ((void (*)(__block_impl *))((__block_impl *)voidBlock)->FuncPtr)((__block_impl *)voidBlock);
        }
        return 0;
    }
    

    从C++的代码开__block int a = 0; 会生成__Block_byref_a_0这个结构体类型。

    在.cpp 文件中有两个函数

    static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);}
    
    static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);}
    

    其中__main_block_copy_0还调用了_Block_object_assign函数。通过源码可以找到这个函数的调用

    void _Block_object_assign(void *destArg, const void *object, const int flags) {
        const void **dest = (const void **)destArg;
        switch (os_assumes(flags & BLOCK_ALL_COPY_DISPOSE_FLAGS)) {
          case BLOCK_FIELD_IS_OBJECT:
            /*******
            id object = ...;
            [^{ object; } copy];
            ********/
    
            _Block_retain_object(object);
            *dest = object;
            break;
    
          case BLOCK_FIELD_IS_BLOCK:
            /*******
            void (^object)(void) = ...;
            [^{ object; } copy];
            ********/
    
            *dest = _Block_copy(object);
            break;
        
          case BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK: // __block | __weak 修饰的变量
          case BLOCK_FIELD_IS_BYREF: //__block修饰的变量
            /*******
             // copy the onstack __block container to the heap
             // Note this __weak is old GC-weak/MRC-unretained.
             // ARC-style __weak is handled by the copy helper directly.
             __block ... x;
             __weak __block ... x;
             [^{ x; } copy];
             ********/
    
            *dest = _Block_byref_copy(object);
            break;
            
          case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT:
          case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK:
            /*******
             // copy the actual field held in the __block container
             // Note this is MRC unretained __block only. 
             // ARC retained __block is handled by the copy helper directly.
             __block id object;
             __block void (^object)(void);
             [^{ object; } copy];
             ********/
    
            *dest = object;
            break;
    
          case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_WEAK:
          case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK  | BLOCK_FIELD_IS_WEAK:
            /*******
             // copy the actual field held in the __block container
             // Note this __weak is old GC-weak/MRC-unretained.
             // ARC-style __weak is handled by the copy helper directly.
             __weak __block id object;
             __weak __block void (^object)(void);
             [^{ object; } copy];
             ********/
    
            *dest = object;
            break;
    
          default:
            break;
        }
    }
    

    这个段代码的就是当block或者Block_byrefs持有对象,就会通过这段代码来进行分配复制,从上面的代码可以知道在执行copy函数调用_Block_object_assign会将a的对象传进来。并且switch里面的枚举如下

    // Runtime support functions used by compiler when generating copy/dispose helpers
    
    // Values for _Block_object_assign() and _Block_object_dispose() parameters
    enum {
        // see function implementation for a more complete description of these fields and combinations
        BLOCK_FIELD_IS_OBJECT   =  3,  // id, NSObject, __attribute__((NSObject)), block, ...对象
        BLOCK_FIELD_IS_BLOCK    =  7,  // a block variable 变量
        BLOCK_FIELD_IS_BYREF    =  8,  // the on stack structure holding the __block variable __block修饰的变量
        BLOCK_FIELD_IS_WEAK     = 16,  // declared __weak, only used in byref copy helpers    __weak修饰的变量
        BLOCK_BYREF_CALLER      = 128, // called from __block (byref) copy/dispose support routines.  
    };
    

    因为当前是__block修饰的就会执行到_Block_byref_copy函数里面去,传进去的是一个结构体.

    static struct Block_byref *_Block_byref_copy(const void *arg) {
        struct Block_byref *src = (struct Block_byref *)arg;
    
        if ((src->forwarding->flags & BLOCK_REFCOUNT_MASK) == 0) {
            // src points to stack
            struct Block_byref *copy = (struct Block_byref *)malloc(src->size);
            copy->isa = NULL;
            // byref value 4 is logical refcount of 2: one for caller, one for stack
            copy->flags = src->flags | BLOCK_BYREF_NEEDS_FREE | 4;
            copy->forwarding = copy; // patch heap copy to point to itself
            src->forwarding = copy;  // patch stack to point to heap copy
            copy->size = src->size;
    
            if (src->flags & BLOCK_BYREF_HAS_COPY_DISPOSE) {
                // Trust copy helper to copy everything of interest
                // If more than one field shows up in a byref block this is wrong XXX
                struct Block_byref_2 *src2 = (struct Block_byref_2 *)(src+1);
                struct Block_byref_2 *copy2 = (struct Block_byref_2 *)(copy+1);
                copy2->byref_keep = src2->byref_keep;
                copy2->byref_destroy = src2->byref_destroy;
    
                if (src->flags & BLOCK_BYREF_LAYOUT_EXTENDED) {
                    struct Block_byref_3 *src3 = (struct Block_byref_3 *)(src2+1);
                    struct Block_byref_3 *copy3 = (struct Block_byref_3*)(copy2+1);
                    copy3->layout = src3->layout;
                }
    
                (*src2->byref_keep)(copy, src);
            }
            else {
                // Bitwise copy.
                // This copy includes Block_byref_3, if any.
                memmove(copy+1, src+1, src->size - sizeof(*src));
            }
        }
        // already copied to heap
        else if ((src->forwarding->flags & BLOCK_BYREF_NEEDS_FREE) == BLOCK_BYREF_NEEDS_FREE) {
            latching_incr_int(&src->forwarding->flags);
        }
        
        return src->forwarding;
    }
    

    这段代码大概的意思是将传进去的结构体重新创建一个,并且赋值相同的内存大小通过

    copy->forwarding = copy; // patch heap copy to point to itself
    src->forwarding = copy;  // patch stack to point to heap copy
    
    WeChat1186a2e029717109b1927b4859a73702.png

    _Block_object_dispose函数调用时机及作用
    当block从堆中移除时就会自动调用__main_block_desc_0中的__main_block_dispose_0函数,__main_block_dispose_0函数内部会调用_Block_object_dispose函数。

    // When Blocks or Block_byrefs hold objects their destroy helper routines call this entry point
    // to help dispose of the contents  
    // 当 Blocks 或 Block_byrefs 持有对象时,它们的销毁助手例程调用此入口点
    // 帮助处理内容
    void _Block_object_dispose(const void *object, const int flags) {
        switch (os_assumes(flags & BLOCK_ALL_COPY_DISPOSE_FLAGS)) {
          case BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK:
          case BLOCK_FIELD_IS_BYREF:
            // get rid of the __block data structure held in a Block
            _Block_byref_release(object);
            break;
          case BLOCK_FIELD_IS_BLOCK:
            _Block_release(object);
            break;
          case BLOCK_FIELD_IS_OBJECT:
            _Block_release_object(object);
            break;
          case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT:
          case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK:
          case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_WEAK:
          case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK  | BLOCK_FIELD_IS_WEAK:
            break;
          default:
            break;
        }
    }
    

    四、Blokc性能分析

    (Block用起来怎样呢?)

    1. block、delegate作用:
    • 1.1能回调传值

    • 1.2使对象与对象之间能通信交互

    • 1.3改变或传递控制链。允许一个类在某些特定时刻通知到其他类,而不需要获取到那些类的指针。可以减少框架复杂度

    1. block优缺点
    • 2.1 block优点:

      省去了写代理的很多代码

      block 更轻型,使用更简单,能够直接访问上下文,这样类中不需要存储临时数据,使用 block 的代码通常会在同一个地方,这样能连贯读代码

    • 2.2 block缺点:

      ① block不够安全,使用 block 时稍微不注意就形成循环引用,导致对象释放不了。这种循环引用,一旦出现就比较难检查出来。

      ② block效率低,block出栈需要将使用的数据从栈内存拷贝到堆内存

      ③ 在多个通信事件的时候,block显得不够直观也不易维护。

    1. block使用场景:

      在1-2个通信事件的时候用block

    2. delegate优缺点:

    • 4.1 delegate优点:

      ① delegate更安全, delegate 的方法是分离开的,不会引用上下文,不容易循环引用

      ② delegate效率高,delegate只是保存了一个对象指针

      ③ 在多个通信事件的时候,delegate显得直观也易维护。

    • 4.2 delegate缺点:

      ① 因方法的声明和实现分离开来,代码的连贯性不是很好,没有 block 好读

      ② 很多时候需要存储一些临时数据

    • 4.3 delegate使用场景:

      3个以上通信事件用delegate

    相关文章

      网友评论

          本文标题:Block 原理浅析

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