首页 > 代码库 > block 解析 - 局部变量

block 解析 - 局部变量

局部变量

block内使用局部变量,一般都是截获变量(只读),截获离block初始化最近的一次的值。
引用官方文档:
  1. Stack (non-static) variables local to the enclosing lexical scope are captured as const variables.Their values are taken at the point of the block expression within the program. In nested blocks, the value is captured from the nearest enclosing scope. 
 
我们做一个测试,了解一下原理
代码如下:
void main1(){    char *_para1="a";    printf("init _para1:%s,%p,%p\n",_para1,_para1,&_para1);    void(^testBlock)(void)=^{        printf("exute _para1:%s,%p,%p\n",_para1,_para1,&_para1);    };    _para1="b";    printf("before _para1:%s,%p,%p\n",_para1,_para1,&_para1);    testBlock();    printf("after _para1:%s,%p,%p\n",_para1,_para1,&_para1);}

 输出一下结果:

init _para1:a,0x97f70,0x27d70988before _para1:b,0x97fa5,0x27d70988exute _para1:a,0x97f70,0x165839e4after _para1:b,0x97fa5,0x27d70988
 
我们可以借助  clang -rewrite-objc 转换.c文件得到.cpp文件,也可以转换.m也可以得到cpp文件(可能会有些报错)
以下是部分转换后的代码
//这里就是block对象的结构
//imp:函数指针对象,FuncPtr指向具体block实现的函数
//_person2:截获的变量
//isa、flags、funcptr、desc后面会说道。
struct __block_impl {  void *isa;  int Flags;  int Reserved;  void *FuncPtr;};
struct __main1_block_impl_0 {  struct __block_impl impl;  struct __main1_block_desc_0* Desc;  char *_para1;  __main1_block_impl_0(void *fp, struct __main1_block_desc_0 *desc, char *__para1, int flags=0) : _para1(__para1) {    impl.isa = &_NSConcreteStackBlock;    impl.Flags = flags;    impl.FuncPtr = fp;    Desc = desc;  }};
//block实现的函数
static void __main1_block_func_0(struct __main1_block_impl_0 *__cself) {  char *_para1 = __cself->_para1; // bound by copy        printf("exute _para1:%s,%p,%p\n",_para1,_para1,&_para1);    }
//block对象的描述信息(大小等等)
static struct __main1_block_desc_0 {  size_t reserved;  size_t Block_size;} __main1_block_desc_0_DATA = { 0, sizeof(struct __main1_block_impl_0)};
//这是objc测试函数test
void main1(){    char *_para1="a";    printf("init _para1:%s,%p,%p\n",_para1,_para1,&_para1);    void(*testBlock)(void)=(void (*)())&__main1_block_impl_0((void *)__main1_block_func_0, &__main1_block_desc_0_DATA, _para1);    _para1="b";    printf("before _para1:%s,%p,%p\n",_para1,_para1,&_para1);    ((void (*)(__block_impl *))((__block_impl *)testBlock)->FuncPtr)((__block_impl *)testBlock);    printf("after _para1:%s,%p,%p\n",_para1,_para1,&_para1);}

简单分析block截获变量:

 1).block初始化
void(*testBlock)(void)=(void (*)())&__main1_block_impl_0((void *)__main1_block_func_0, &__main1_block_desc_0_DATA, _para1);
传入了参数:函数指针、block描述、外部变量_para1,这时候在block内部对_para1进行了引用,即block的结构体成员_para1引用了的是 这个地址 0x97f70
 : _para1(__para1) 

 之后又对_para1做了修改,重新指向了 0x97fa5这块内存,但是不会影响block结构体成员_para1,因为成员_para1指向的是0x97f70。

2).执行block
((void (*)(__block_impl *))((__block_impl *)testBlock)->FuncPtr)((__block_impl *)testBlock);

其实还是调用了block对象里的函数对象(_block_imp1)的函数指针(FuncPtr) 所指向的函数__main1_block_func_0,并把block自己作为参数传递进去。

static void __main1_block_func_0(struct __main1_block_impl_0 *__cself) {  char *_para1 = __cself->_para1; // bound by copy        printf("exute _para1:%s,%p,%p\n",_para1,_para1,&_para1);    }

值得注意的的是,我们在执行之前对_para1修改,但是并没有对block内部产生影响(只是把局部变量_para1重新指向另一块内存(0x97fa5)),而在构造block的时候,block对象内部对截获的变量做了引用(引用的是0x97f70),放在(char *_para1)里,执行block的时候,直接调用函数__main1_block_func_0,函数内部,是引用了block结构体的成员_para1,所以会有这样的结果。