博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
iOS Block实现探究
阅读量:6983 次
发布时间:2019-06-27

本文共 4485 字,大约阅读时间需要 14 分钟。

hot3.png

使用clang的rewrite-objc filename 可以将有block的c代码转换成cpp代码。从中可以看到block的实现。

#include 
int main(){ void (^blk)(void) = ^{ printf("Block\n"); }; blk(); return 0;}
使用clang rewrite-objc以后会看到block的实现
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;    impl.Flags = flags;    impl.FuncPtr = fp;    Desc = desc;  }};

可以看到其实block是一个正常的OC类

来看看block是怎样访问外部变量的

int main(){  int dmy = 256;  int val = 10;  const char *fmt = "val = %d\n";  void (^blk)(void) = ^{    printf(fmt, val);  };  return 0;}

转换之后,可以看到

struct __main_block_impl_0 {  struct __block_impl impl;  struct __main_block_desc_0* Desc;  const char *fmt;  int val;  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, const char *_fmt, int _val, int flags=0) : fmt(_fmt), val(_val) {    impl.isa = &_NSConcreteStackBlock;    impl.Flags = flags;    impl.FuncPtr = fp;    Desc = desc;  }};
int main(){  int dmy = 256;  int val = 10;  const char *fmt = "val = %d\n";  void (*blk)(void) = (void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, fmt, val);  return 0;}

block的变量会被复制进block中

如果当block要改变传入的变量值怎么办?首先看一下全局变量和本地静态变量

int global_val = 1;static int static_global_val = 2;int main(){  static int static_val = 3;  void(^blk)(void) = ^{    global_val *= 1;    static_global_val *= 2;    static_val *= 3;  };  blk();  return 0;}
struct __main_block_impl_0 {  struct __block_impl impl;  struct __main_block_desc_0* Desc;  int *static_val;  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_static_val, int flags=0) : static_val(_static_val) {    impl.isa = &_NSConcreteStackBlock;    impl.Flags = flags;    impl.FuncPtr = fp;    Desc = desc;  }};static void __main_block_func_0(struct __main_block_impl_0 *__cself) {  int *static_val = __cself->static_val; // bound by copy    global_val *= 1;    static_global_val *= 2;    (*static_val) *= 3;  }

由于全局变量是在Data Section中,所以直接可以访问。局部静态变量是通过将其指针传入到block中,block就可以对其值进行修改。

然后看一下__block修饰符变量

int main(){  __block int val = 10;  void (^blk)(void) = ^{val = 1;};  blk();  return 0;}
struct __main_block_impl_0 {  struct __block_impl impl;  struct __main_block_desc_0* Desc;  __Block_byref_val_0 *val; // by ref  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_val_0 *_val, int flags=0) : val(_val->__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_val_0 *val = __cself->val; // bound by ref(val->__forwarding->val) = 1;}static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->val, (void*)src->val, 8/*BLOCK_FIELD_IS_BYREF*/);}

__block修饰符的变量,会生成一个__Block_byref_val_0的struct,然后通过访问其__forwarding来访问val值。因为Block有可能是在stack或者heap中,所以用__forwarding来访问。之所以会将__block单独生成一个struct是因为可能该变量会被多个block使用。

Block分三种类型

0) NSConcreteStackBlock    --stack

1) NSConcreteGlobalBlock   --data area

2) NSConcreteMallocBlock   --heap

自动copy block

当开启ARC时,在某些情况编译器会自动copy block,从stack到heap。

typedef int (^blk_t)(int);blk_t func(int rate){  return ^(int count){return rate * count;};}
blk_t func(int rate){  blk_t tmp = &__func_block_impl_0(    __func_block_func_0, &__func_block_desc_0_DATA, rate);  tmp = objc_retainBlock(tmp);  return objc_autoreleaseReturnValue(tmp);}

有些情况,编译器是无法检测是否应该copy block: 

当block作为参数传递到方法或函数中。

但是,如果该方法或函数在内部copy,就不用手动再copy:

0)cocoa framework方法, 有usingBlock

1) GCD API

- (id) getBlockArray{  int val = 10;  return [[NSArray alloc] initWithObjects:     [^{NSLog(@"blk0:%d", val);} copy],     [^{NSLog(@"blk1:%d", val);} copy], nil];}

__forwarding

当block从stack copy到 heap中时,block中用到的__block也会copy到heap中,并且copy到heap中的block拥有该__block。

__block int val = 0;void (^blk)(void) = [^{++val;} copy];++val;blk();NSLog(@"%d", val);

在block和外的++val都会变成 ++(val.__forwarding->val);

当block copy到heap中后, stack中的__forwarding会指向heap中的__block, heap中的__forwarding会指向自己的__block值,这样保证了__forwarding指向的是同一个变量值。

Block什么时候会copy到heap中

0)对block调用copy方法。

1)block作为一个函数的返回值。 编译器自动copy

2)赋值给id或block type class 有__strong 修饰符的成员变量。   编译器自动copy

3)usingBlock, GCD API。  在函数内copy

什么时候用该copy block?

0) block是函数返回值

1) block赋值给id或block type class 有__strong 修饰符的成员变量。 

2)3)usingBlock, GCD API。  在函数内copy

转载于:https://my.oschina.net/u/566401/blog/219568

你可能感兴趣的文章
你用过 PropTypes 的这些类型检查么?
查看>>
Java™ 教程(泛型、继承和子类型)
查看>>
如何优雅的构建排序公式
查看>>
SpringCloud核心教程 | 第二篇: 使用Intellij中的maven来快速构建Spring Cloud工程
查看>>
node中 模块导入和导出的探究
查看>>
Phalcon入门教程之模型CURD(2)
查看>>
《HTML5 canvas开发详解(第2版)》——2.12 检查一个点是否在当前路径
查看>>
《深入理解Scala》——第2章,第2.1节学习使用Scala交互模式(REPL)
查看>>
《21天学通HTML+CSS+JavaScript Web开发(第7版)》——1.7 作业
查看>>
【Android】用MediaRecorder录制视频太短崩的问题
查看>>
一个数组实现两个栈
查看>>
phonegap+jquerymobile开发android的心得(4)
查看>>
js获取url传递参数
查看>>
jQuery中bind方法与live方法区别
查看>>
高德地图POI查找
查看>>
磁盘格式化
查看>>
Fedora 11 安装指南-12
查看>>
iOS中的一些小知识点
查看>>
Oracle 11g RAC 添加新节点及故障解决案例
查看>>
docker logstash 使用
查看>>