Block 原理浅析

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

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