iOS 深入理解Block底层实现原理

## Block拷贝

block的属性需要使用copy修饰,如果block一旦没有copy操作就不会在堆上,无法对block生命周期进行控制。

使用注意:循环引用的问题。

block在修改NSMutableArray,不需要添加__block,因为修改内容也是对数组的使用,只有对对象赋值的时候才需要__block。

## __Block

当使用__block修饰符时,基本数据类型 a 被转换成了__Block_byref_a_0结构体。__Block_byref_a_0结构体中带有 isa指针,说明它也是一个对象。

当block修改变量时,会调用下面代码:

__block修饰变量用结构体成员变量__forwarding可以实现无论 __block变量配置在栈上还是堆上都能够正确的访问 __block变量。

__forwarding指针始终指向自己。

当__block int a在栈中的时候,__forwarding指向的是栈中的自己。

当 a 拷贝到堆中时候,__forwarding指向的是堆中的自己。

__block 截获自动变量是实质就是:即截获变量指针。自动变量的值被保存到block结构体实例(Block本身)中。

这个结构体名字叫:__main_block_impl_0。

## 链式编程

### ReactiveCocoa

### Masonry

### RxSwift

### SnapKit

## 类型

注意:在 ARC 开启的情况下,将只会有 NSConcreteGlobalBlock 和 NSConcreteMallocBlock 类型的 block。因为Block从栈拷贝到堆中。

### NSConcreteGlobalBlock:block内部没有访问外部auto变量.

struct __Block_byref_a_0 {

 void *__isa;

__Block_byref_a_0 *__forwarding;

 int __flags;

 int __size;

 int a;

};

struct __Block_byref_obj_1 {

 void *__isa;

__Block_byref_obj_1 *__forwarding;

 int __flags;

 int __size;

 void (*__Block_byref_id_object_copy)(void*, void*);

 void (*__Block_byref_id_object_dispose)(void*);

 NSObject *obj;

};

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {

 __Block_byref_a_0 *a = __cself->a; // bound by ref

 __Block_byref_obj_1 *obj = __cself->obj; // bound by ref



      (a->__forwarding->a)++;

      (obj->__forwarding->obj) = ((NSMutableArray * _Nonnull (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSMutableArray"), sel_registerName("array"));



      printf("截获后--:%d", (a->__forwarding->a));

    }

NSGlobalBlock是位于全局区的block,它是设置在程序的数据区域(.data区)中。

以下两种情况下,block为NSConcreteGlobalBlock

a.记述全局变量的地方有block语法时。

b.block语法的表达式中不使用任何外部变量时。

- 全局区block底层原理


  struct __block_impl {

   void *isa;//指向所属类的指针,也就是block的类型

   int Flags;//标志变量,在实现block的内部操作时会用到

   int Reserved;//保留变量

   void *FuncPtr;//block执行时调用的函数指针

  };

  struct __globalBlock_block_impl_0 {

   struct __block_impl impl;

   struct __globalBlock_block_desc_0* Desc;

   __globalBlock_block_impl_0(void *fp, struct __globalBlock_block_desc_0 *desc, int flags=0) {

    impl.isa = &_NSConcreteGlobalBlock;

    impl.Flags = flags;

    impl.FuncPtr = fp;

    Desc = desc;

   }

  };

  static void __globalBlock_block_func_0(struct __globalBlock_block_impl_0 *__cself) {

  

    static int g = 10;

    printf("%d",g);

  }

  

  static struct __globalBlock_block_desc_0 {

   size_t reserved;

   size_t Block_size;

  } __globalBlock_block_desc_0_DATA = { 0, sizeof(struct __globalBlock_block_impl_0)};

  static __globalBlock_block_impl_0 __global_globalBlock_block_impl_0((void *)__globalBlock_block_func_0, &__globalBlock_block_desc_0_DATA);

  void (*globalBlock)() = ((void (*)())&__global_globalBlock_block_impl_0);

  int main(int argc, const char * argv[]) {

    return 0;

  }

  static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };

### NSConcreteStackBlock

NSStackBlock是位于栈区,超出变量作用域,栈上的Block以及 __block变量都被销毁。只在MRC存在,ARC不存在,ARC只要是block内部访问量而且有强引用指向该block(或者作为函数返回值)就会自动将__NSStackBlock类型copy到堆上。

- 栈区block底层原理


  struct __block_impl {

   void *isa;

   int Flags;

   int Reserved;

   void *FuncPtr;

  };

  struct __main_block_impl_0 {

   struct __block_impl impl;

   struct __main_block_desc_0* Desc;

   __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {

    impl.isa = &_NSConcreteStackBlock;//这个是C++的结构体 该函数是C++结构体的初始化函数

  

    impl.Flags = flags;

    impl.FuncPtr = fp;

    Desc = desc;

   }

  };

  static void __main_block_func_0(struct __main_block_impl_0 *__cself) {

  

  

    }

  

  static struct __main_block_desc_0 {//这是对__main_block_impl_0结构体的描述信息

   size_t reserved;

   size_t Block_size;

  } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};

  int main(int argc, const char * argv[]) {

  

    printf("Hello, World!\n");

    int a = 10;

    printf("%d", a);

    void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));

    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);

    return 0;

  }

### NSConcreteMallocBlock

NSMallocBlock是位于堆区,在变量作用域结束时不受影响。保存在堆中的 block,当引用计数为 0 时会被销毁。ARC环境下只要访问了外部auto变量而且有强引用指向该block(或者作为函数返回值)就会自动将__NSStackBlock类型copy到堆上。

堆中的block无法直接创建,其需要由_NSConcreteStackBlock类型的block拷贝而来(也就是说block需要执行copy之后才能存放到堆中)。由于block的拷贝最终都会调用_Block_copy_internal函数。

- 堆区block底层原理


  __block int a = 10;

      printf("截获前:%d", a);

      void (^block)(void) = [^{

        a++;

        printf("截获后--:%d", a);

      } copy];

      a++;

      printf("截获后:%d", a);

      block();

  

  //这样的话block的isa就是NSConcreteMallocBlock

  typedef void (*blk_t)(void);

  

  

  struct __getBlockArray0_block_impl_0 {

   struct __block_impl impl;

   struct __getBlockArray0_block_desc_0* Desc;

   int val;

   __getBlockArray0_block_impl_0(void *fp, struct __getBlockArray0_block_desc_0 *desc, int _val, int flags=0) : val(_val) {

    impl.isa = &_NSConcreteStackBlock;

    impl.Flags = flags;

    impl.FuncPtr = fp;

    Desc = desc;

   }

  };

  static void __getBlockArray0_block_func_0(struct __getBlockArray0_block_impl_0 *__cself) {

   int val = __cself->val; // bound by copy

  NSLog((NSString *)&__NSConstantStringImpl__var_folders_77__vw6s40s54v9p9d9zb4f_3br0000gn_T_main_c1dee6_mi_0,val);}

  

  static struct __getBlockArray0_block_desc_0 {

   size_t reserved;

   size_t Block_size;

  } __getBlockArray0_block_desc_0_DATA = { 0, sizeof(struct __getBlockArray0_block_impl_0)};

  

  struct __getBlockArray0_block_impl_1 {

   struct __block_impl impl;

   struct __getBlockArray0_block_desc_1* Desc;

   int val;

   __getBlockArray0_block_impl_1(void *fp, struct __getBlockArray0_block_desc_1 *desc, int _val, int flags=0) : val(_val) {

    impl.isa = &_NSConcreteStackBlock;

    impl.Flags = flags;

    impl.FuncPtr = fp;

    Desc = desc;

   }

  };

  static void __getBlockArray0_block_func_1(struct __getBlockArray0_block_impl_1 *__cself) {

   int val = __cself->val; // bound by copy

  NSLog((NSString *)&__NSConstantStringImpl__var_folders_77__vw6s40s54v9p9d9zb4f_3br0000gn_T_main_c1dee6_mi_1,val);}

  

  static struct __getBlockArray0_block_desc_1 {

   size_t reserved;

   size_t Block_size;

  } __getBlockArray0_block_desc_1_DATA = { 0, sizeof(struct __getBlockArray0_block_impl_1)};

  NSArray *getBlockArray0() {

    int val =10;

    return ((NSArray * _Nonnull (*)(id, SEL, ObjectType _Nonnull, ...))(void *)objc_msgSend)((id)objc_getClass("NSArray"), sel_registerName("arrayWithObjects:"), (id _Nonnull)((void (*)())&__getBlockArray0_block_impl_0((void *)__getBlockArray0_block_func_0, &__getBlockArray0_block_desc_0_DATA, val)), ((void (*)())&__getBlockArray0_block_impl_1((void *)__getBlockArray0_block_func_1, &__getBlockArray0_block_desc_1_DATA, val)), __null);

  }

  struct __Block_byref_a_0 {

   void *__isa;

  __Block_byref_a_0 *__forwarding;

   int __flags;

   int __size;

   int a;

  };

  

  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;

   }

  };

  static void __main_block_func_0(struct __main_block_impl_0 *__cself) {

   __Block_byref_a_0 *a = __cself->a; // bound by ref

  

        (a->__forwarding->a)++;

        printf("截获后--:%d", (a->__forwarding->a));

      }

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

  

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

  } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};

  int main(int argc, char * argv[]) {

  

    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 

  

  

      __attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 10};

      printf("截获前:%d", (a.__forwarding->a));

      void (*block)(void) = (void (*)())((id (*)(id, SEL))(void *)objc_msgSend)((id)((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344)), sel_registerName("copy"));

      (a.__forwarding->a)++;

      printf("截获后:%d", (a.__forwarding->a));

      ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);

  

    }

    return 0;

  

  }

## Block的本质

block本质上也是一个OC对象,它内部也有个isa指针

block是封装了函数调用以及函数调用环境的OC对象

## 引用循环

如果在Block中使用附有__strong修饰符的对象类型的自动变量,那么当Block从栈复制到堆时,该对象为Block所持有。这样容易引起循环引用。

在ARC环境下,只要将block赋值给一个变量,那么这个block就将被拷贝到堆上,这是编译器的优化。

  • __weak 
        Member *member = [[Member alloc] init];
        member.height = 120;
        __weak typeof(member) weakMember = member;
        member.didTappedBlock = ^{
            printf("member height == %f\n", weakMember.height);
        };
  • __block
        __block Member *member = [[Member alloc] init];
        member.height = 120;
        member.didTappedBlock = ^{
            printf("member height == %f\n", member.height);
            member = nil;
        };
        member.didTappedBlock();

        1.必须在Block内使对象置为nil

        2.必须调用Block,如果不调用Block,永远存在循环引用

  • __unsafe_unretained
        Member *member = [[Member alloc] init];
        member.height = 120;
        __unsafe_unretained typeof(member) weakMember = member;
        member.didTappedBlock = ^{
            printf("member height == %f\n", weakMember.height);
        };

在MRC环境下:

1.MRC不支持__weak弱引用

2.MRC __block不会对对象产生引用(retain)

  • __unsafe_unretained
        Member *member = [[Member alloc] init];
        member.height = 120;
        __unsafe_unretained typeof(member) weakMember = member;
        member.didTappedBlock = ^{
            printf("member height == %f\n", weakMember.height);
        };
        NSLog(@"end=================%@==",[member.didTappedBlock class]);
        [member release];
  • __block 
        __block Member *member = [[Member alloc] init];
        member.height = 120;
        member.didTappedBlock = ^{
            printf("member height == %f\n", member.height);
        };
        NSLog(@"end=================%@==",[member.didTappedBlock class]);
        [member release];

例外可以传入一个局部变量,这样就不会导致引用循环了。

## 截获自动变量

这个其实也是block底层原理

利用__block

有三种情况不用__block也可以截获变量的值:

静态全局变量

全局变量

静态变量

*XMind: ZEN - Trial Version*

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值