现在的位置: 首页 > 综合 > 正文

移动开发ios之Block研究总结分享

2019年11月13日 综合 ⁄ 共 3420字 ⁄ 字号 评论关闭

  Block的好处,我总结了下主要有2点:1.用于回调特别方便,2.可以延长对象的作用区域。但是,Block的内存管理这个模块一直不是很清楚,这个周末好好的看了下Block的原理,有些许心得。

  为了性能,默认Block都是分配在stack上面的,所以它的作用区域就是当前函数。

  #include

  int main()

  {

  int i = 1024;

  void (^blk)(void) = ^ {

  printf("%d\n", i);

  };

  blk();

  return 0;

  }

  在blk这个block里面是不能修改i的。Why?我们可以通过clang看看编译器处理后的这块代码

  struct __main_block_impl_0 {

  struct __block_impl impl;

  struct __main_block_desc_0* Desc;

  int i;

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

  impl.isa = &_NSConcreteStackBlock;

  impl.Flags = flags;

  impl.FuncPtr = fp;

  Desc = desc;

  }

  };

  static void __main_block_func_0(struct __main_block_impl_0 *__cself) {

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

  printf("%d\n", i);

  }

  static struct __main_block_desc_0 {

  unsigned long reserved;

  unsigned long Block_size;

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

  int main()

  {

  int i = 1024;

  void (*blk)(void) = (void (*)(void))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, i);

  ((void (*)(struct __block_impl *))((struct __block_impl *)blk)->FuncPtr)((struct __block_impl *)blk);

  return 0;

  }

  struct __block_impl是Block的一个内部结构体,原型是

  struct __block_impl {

  void *isa;

  int Flags;

  int Reserved;

  void *FuncPtr;

  };

  每个block都有个默认的构造函数

  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _i, int flags=0) : i(_i) 所以只能读取i,而不能修改i,当你试图修改它时,编译器就在预处理阶段直接报错。

  只要在i前加__Block变量就可以在Block里面修改i值了,此时由值类型变为引用类型

  struct __main_block_impl_0 {

  struct __block_impl impl;

  struct __main_block_desc_0* Desc;

  int *i;

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

  impl.isa = &_NSConcreteStackBlock;

  impl.Flags = flags;

  impl.FuncPtr = fp;

  Desc = desc;

  }

  };

  static void __main_block_func_0(struct __main_block_impl_0 *__cself) {

  int *i = __cself->i; // bound by copy

  printf("%d\n", (*i));

  }

  static struct __main_block_desc_0 {

  unsigned long reserved;

  unsigned long Block_size;

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

  int main()

  {

  static int i = 1024;

  void (*blk)(void) = (void (*)(void))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &i);

  ((void (*)(struct __block_impl *))((struct __block_impl *)blk)->FuncPtr)((struct __block_impl *)blk);

  return 0;

  }

  上面的代码块是将int i的类型修改为__Block int i = 1024;后编译器生成代码块,可以看到__main_block_impl_0中的 i类型已经改变为int *,所以我们可以修改它的值。

  所以只要没对Block进行copy操作,它一直存在stack里面。不管是否有__block修饰符

  要想延长Block的作于域,我们可以对它进行copy操作,apple提供的接口是Block_Copy()方法

  View Code

  通过观察apple提供的block源码,我们可以看到copy方法将block从statck拷贝到heap里面,所以它的作用区域延长了

  待完成:1.block与oc的混合

  2.__block修饰符与oc的混合

  总结

  1.block默认都是分配在stack,当copy后,它移到heap里

  2.block中的变量默认是不能修改的,只有添加__Block修饰符后才能修改

  3.block中有oc对象时,会_Block_retain_object(object),直到block销毁后才会_Block_release_object(object);

  4.对block进行copy时

  If you access an instance variable by reference, a strong reference is made to self;

  If you access an instance variable by value, a strong reference is made to the variable.

  它会将self进行copy,此时改对象的dealloc方法不会执行(因为它的引用计数归0),解决此问题有2种方法:在block执行完成后面立即Block_Release(),或者将改变量声明为__Block类型(Why?)

  继续补充block的oc的混合

  View Code

  编译器生成代码为

  View Code

  blk的原型为

  void (*blk)(void) = (void (*)(void))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, str, 570425344);

  其中570425344 = BLOCK_HAS_COPY_DISPOSE | BLOCK_HAS_DESCRIPTOR;

  通过构造函数,我们看到仍然是值传递,所以blk中是能不修改str的。

  至于上面的__main_block_copy_0和__main_block_dispose_0 就是用于Block_Copy()和Block_Release();

  当我将str的类型修改为__block NSMutableString时,生成如下代码

  View Code

  可以看到str的类型实际上是__Block_byref_str_0,其中33554432 = BLOCK_HAS_COPY_DISPOSE = (1 << 25)   而blk的构造函数中传递的是__Block_byref_str_0类型的指针,所以我们能在blk中修改str

抱歉!评论已关闭.