block变量

上一篇 讲的是block静态变量的特性,这里我们来看一下_block变量。引用官方:

You can specify that an imported variable be mutable—that is, read-write— by applying the __block storage type modifier. __blockstorage is similar to, but mutually exclusive of, the registerauto, and static storage types for local variables.
通过指定__block存储类型修饰符,可以读写。__block存储是类似的,但相互排斥的,寄存器,自动变量,和局部变量和静态变量类型(后面这句不太理解)。 
我们来写个例子,用__block修饰局部变量(对于全局变量是无效的,没有任何作用,全局变量成员变量本身就可以在block内部修改,详细看这一篇 ):
void main1()
{
__block 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,0x47f4,0xbfffc9c0
before _para1:b,0x4829,0x8da4580
exute _para1:b,0x4829,0x8da4580
after _para1:b,0x4829,0x8da4580 这里加了__block修饰符。通过日志可以看出,block内部_para1的地址、值和执行前的_para1一样,在block初始化后,对变量_para1的修改,可以同步到block内,block内并不是截获了变量的值。我们可以看下通过clang 转换后的后的cpp代码: //block 的实现函数的对象
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
//block 引用的参数对象(每一个参数生成一个结构体)
struct __Block_byref__para1_0 {
void *__isa;
__Block_byref__para1_0 *__forwarding;
int __flags;
int __size;
char *_para1;
};
//block对象
struct __main1_block_impl_0 {
struct __block_impl impl;
struct __main1_block_desc_0* Desc;
__Block_byref__para1_0 *_para1; // by ref
__main1_block_impl_0(void *fp, struct __main1_block_desc_0 *desc, __Block_byref__para1_0 *__para1, int flags=) : _para1(__para1->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
//block 的实现
static void __main1_block_func_0(struct __main1_block_impl_0 *__cself) {
__Block_byref__para1_0 *_para1 = __cself->_para1; // bound by ref printf("exute _para1:%s,%p,%p\n",(_para1->__forwarding->_para1),(_para1->__forwarding->_para1),&(_para1->__forwarding->_para1));
}
//其他函数(copy、dispose:为了管理_Block_byref__para1_0 结构体变量的内存)
static void __main1_block_copy_0(struct __main1_block_impl_0*dst, struct __main1_block_impl_0*src) {_Block_object_assign((void*)&dst->_para1, (void*)src->_para1, /*BLOCK_FIELD_IS_BYREF*/);}

static void __main1_block_dispose_0(struct __main1_block_impl_0*src) {_Block_object_dispose((void*)src->_para1, /*BLOCK_FIELD_IS_BYREF*/);}

static struct __main1_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __main1_block_impl_0*, struct __main1_block_impl_0*);
void (*dispose)(struct __main1_block_impl_0*);
} __main1_block_desc_0_DATA = { , sizeof(struct __main1_block_impl_0), __main1_block_copy_0, __main1_block_dispose_0};
//这是我们的测试函数
void main1()
{
__attribute__((__blocks__(byref))) __Block_byref__para1_0 _para1 = {(void*),(__Block_byref__para1_0 *)&_para1, , sizeof(__Block_byref__para1_0), "a"};
printf("init _para1:%s,%p,%p\n",(_para1.__forwarding->_para1),(_para1.__forwarding->_para1),&(_para1.__forwarding->_para1));
void(*testBlock)(void)=(void (*)())&__main1_block_impl_0((void *)__main1_block_func_0, &__main1_block_desc_0_DATA, (__Block_byref__para1_0 *)&_para1, );
(_para1.__forwarding->_para1)="b";
printf("before _para1:%s,%p,%p\n",(_para1.__forwarding->_para1),(_para1.__forwarding->_para1),&(_para1.__forwarding->_para1));
((void (*)(__block_impl *))((__block_impl *)testBlock)->FuncPtr)((__block_impl *)testBlock);
printf("after _para1:%s,%p,%p\n",(_para1.__forwarding->_para1),(_para1.__forwarding->_para1),&(_para1.__forwarding->_para1));
}

发现和不加__block不太一样,多了一个结构体(_Block_byref__para1_0)


struct __Block_byref__para1_0 {
void *__isa;
__Block_byref__para1_0 *__forwarding;
int __flags;
int __size;
char *_para1;
};

这个结构体就是用__block 声明的变量,声明__block变量_para1 转换后的代码如下:

__attribute__((__blocks__(byref))) __Block_byref__para1_0 _para1 = {(void*),(__Block_byref__para1_0 *)&_para1, , sizeof(__Block_byref__para1_0), "a"};

其实就是生成一个_Block_byref__para1_0对象,__forwarding 指针指向变量结构体自己

void(*testBlock)(void)=(void (*)())&__main1_block_impl_0((void *)__main1_block_func_0, &__main1_block_desc_0_DATA, (__Block_byref__para1_0 *)&_para1, );

初始化block,传递结构体变量_para1、函数地址、描述信息等参数,block对象的成员 _para1 引用了_para1结构体的地址,这样就可以修改_para1,而且外界对_para1结构体的修改都可以同步到block对象的成员_para1中,修改代码如下:

(_para1.__forwarding->_para1)="b";

直接通过__Block_byref__para1_0 结构体的成员__forwarding(指向自己)取得_para1成员的地址,然后就可以读写,达到了修改变量的目的。

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

执行block,依然是通过调用函数指针 FuncPtr 实现,并传递block自身。函数体代码如下:

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

在函数体内,通过block对象的成员_para1(__Block_byref__para1_0的指针,再通过自身的成员__forwarding指针来获取_para1。

我的理解就是指针引用的关系,不知道为什么要加一个__forwarding指针 ,直接去引用自身的成员 _para1难道不行吗?希望大神赐教。

block 解析 - block变量的更多相关文章

  1. block 解析 - 成员变量

    回顾 在 上一篇 中我们讲了截获变量特性,对于局部变量,变量不加__block修饰符,在block内部是无法修改变量的值.而且 对值类型的修改,如果block初始化后,无法同步到block内部 对于指 ...

  2. block 解析 - 形参变量

    block形参 之前漏了一篇block形参的介绍,这里给补上. block形参就是定义block带的参数,和函数的参数使用一样,我们可以在block随意使用修改block形参. 我们来看个例子: 我们 ...

  3. block 解析 - 静态变量

    静态变量 上一篇 我们了解了block全局变量的使用,静态变量和全局变量一样,可以直接在block内部使用,也可以在block内部修改 引用官方文档: Global variables are acc ...

  4. 深入研究Block捕获外部变量和__block实现原理

    Blocks是C语言的扩充功能,而Apple 在OS X Snow Leopard 和 iOS 4中引入了这个新功能“Blocks”.从那开始,Block就出现在iOS和Mac系统各个API中,并被大 ...

  5. Block解析(iOS)

    1. 操作系统中的栈和堆 我们先来看看一个由C/C++/OBJC编译的程序占用内存分布的结构: 栈区(stack):由系统自动分配,一般存放函数参数值.局部变量的值等.由编译器自动创建与释放.其操作方 ...

  6. block捕获自动变量和对象

    一.捕获自动变量值 首先看一个经典block面试题: int val = 10; void (^blk)(void) = ^{printf("val=%d\n",val);}; v ...

  7. block 解析 - 内存

    block结构体相应的也有一个成员引用,这样会增加对局部变量的 _para1引用,在Block销毁的时候引用就释放掉了 我们了解到了用__block修饰的变量,可以在block内部修改,__block ...

  8. block 解析 - 局部变量

    局部变量 block内使用局部变量,一般都是截获变量(只读),截获离block初始化最近的一次的值. 引用官方文档: Stack (non-static) variables local to the ...

  9. block 解析 - 简介

    简介 block 类似标准的c函数,除了一些函数体一些可执行的代码,还可以把变量绑定到自动栈或者托管堆上.....和js里的闭包.c# lambda表达式有些类似,实质是一个函数指针.与函数指针的区别 ...

随机推荐

  1. 关于android源码中的APP编译时引用隐藏的API出现的问题

    今天在编译android源码中的计算器APP时发现,竟然无法使用系统隐藏的API,比如android.os.ServiceManager中的API,引用这个类时提示错误,记忆中在android源码中的 ...

  2. DBCC CHECKDB用法 手工修复数据库

          快速修复 DBCC CHECKDB ('数据库名', REPAIR_FAST)      重建索引并修复 DBCC CHECKDB ('数据库名', REPAIR_REBUILD) 如果必 ...

  3. android程序报错“error launching activity com.android.ddmlib.shellcommandunresponsiveexception”的解决方式

    今天在调试android程序的时候,因为是NDK开发,要先编译.so库再打包下载到android模拟器,所以花费的时间比較长.控制台就会报例如以下错误: error launching activit ...

  4. 你能相信吗?这些都是由一个DIV元素实现的动画,纯CSS3技术

    http://www.webhek.com/misc/css-loaders

  5. Arduino 跷跷板(2016-01-04)

    前言这是参加社区活动,用赠送的 LilyPad 来做小实验,也体验了一把艺术的LilyPad!本来是申请 nano 的,不知道怎么的就出错啦,申请成 LilyPad 了,这个实验应该用 nano 比较 ...

  6. UVa----------1594(Ducci Sequence)

    题目: 1594 - Ducci Sequence Asia - Seoul - 2009/2010A Ducci sequence is a sequence of n-tuples of inte ...

  7. Qt SQL Programming 部分翻译

    简介:      Qt SQL 是 Qt 的重要模块之一,为了方便,Qt 对 SQL 进行了一系列的封装,并将 SQL API 分为如下三层:      (1)驱动层      (2)SQL API ...

  8. 简单的java缓存实现

    扫扫关注"茶爸爸"微信公众号 坚持最初的执着,从不曾有半点懈怠,为优秀而努力,为证明自己而活. 提到缓存,不得不提就是缓存算法(淘汰算法),常见算法有LRU.LFU和FIFO等算法 ...

  9. 生成唯一32位ID编码代码Java(GUID)

    源码下载链接:http://pan.baidu.com/s/1jGCEWlC 扫扫关注"茶爸爸"微信公众号 坚持最初的执着,从不曾有半点懈怠,为优秀而努力,为证明自己而活. /* ...

  10. commons-logging和slf4j都是日志的接口

    过上面的图,可以简单的理清关系! commons-logging和slf4j都是日志的接口,供用户使用,而没有提供实现! log4j,logback等等才是日志的真正实现. 当我们调用接口时,接口的工 ...