Block
2016-05-07 12:28
330 查看
block的定义
block和函数有很多相同点:可以保存代码
有返回值
有形参
调用方式一样
没有返回值,没有形参的block:
// 定义block void (^block)() = ^{ NSLog(@"------block----"); }; // 直接调用 block();
有形参,有返回值的block:
// 求和block int (^sumBlock)(int ,int) = ^(int a, int b){ return a + b; };
其实由此可以看出,block的定义和指向函数的指针非常像:
// sum函数 int sum(int a, int b) { return a + b; } - (void)test { // 指针p指向sum函数 int (*p)(int, int) = sum; }
block对变量的访问
局部变量
block在访问局部变量的时候,首先会拷贝它,然后内部会增加一个该类型的成员变量,用来存储这个拷贝进来局部变量,但是这次拷贝只是一次“值传递”,block 默认是将其复制到其数据结构中来实现访问的,这就意味着我们不能修改该局部变量!因此我们可以通过指针来进行地址传递:
- (void)test { int a; int *p = &a; void (^block)() = ^(){ *p = 10; }; }
但需要注意的是:变量a的生命周期是和方法test相关联的,当test运行结束,栈随之销毁,变量a也会销毁,这个时候p就变成了野指针。如果block是作为参数或者返回值,这些类型就会跨栈,再次调用就会引发野指针错误!
因此可以在声明变量的时候加上__block修饰符,这样就可以在块内修改了!当加上__block修饰符后,block内部就会拷贝其引用地址来实现访问的。
(静态)全局变量
因为全局变量都是在“静态数据存储区”,在程序结束前不会销毁,所以block直接访问了对应的变量(可以直接修改变量值),并没有给它预留位置。静态局部变量
其实它和全局变量是一样的,唯一的不同就是作用域!block的内部结构
block本身也可视为对象,在存放block的内存区域中,首个变量是指向Class对象的指针,该指针叫做isa。其余内存里含有块对象正常运行所需的各种信息,其内存布局如下(该图在Effective Objective-C 2.0中的第37条):该结构在apple的开源代码中也有写。
/* Revised new layout. */ struct Block_descriptor { unsigned long int reserved; unsigned long int size; // 块内存大小 void (*copy)(void *dst, void *src); // 复制函数 void (*dispose)(void *); // 销毁函数 }; struct Block_layout { void *isa; int flags; int reserved; void (*invoke)(void *, ...); struct Block_descriptor *descriptor; /* Imported variables. */ };
isa:指向该block的类Class,同时isa也是OC对象的标识,这就说明block也是对象
flags:按bit位表示一些block的附加信息,比如判断block类型、判断block引用计数、判断block是否需要执行辅助函数等
reserved:保留变量,留给以后升级使用
invoke:函数指针,指向block的实现代码
descriptor:指向结构体的指针,每一个block都包含此结构体,其中声明了block的总体大小,copy和dispose两个辅助函数所对应的函数指针,copy函数会retain住已经拷贝的对象,而dispose函数则是release对象。
其次,block还会把它所访问的所有变量都拷贝一份,这个变量在descriptor变量后面,捕获了多少个变量,就要占据多少内存空间。
需要注意的是:invoke函数为何要把block对象当做参数传进来呢?原因就在于,执行block时,要从内存中把这些访问到的变量读出来。
block的类型
在OC中,一共有三种类型的block:_NSConcreteGlobalBlock:全局的静态block,这种block不会访问任何状态(比如外部的变量等),运行时也无需有任何状态来参与。block所使用的整个内存区域,在编译期就已经完全确定了,因此全局block是声明在全局内存中。同时,全局block的copy操作是一个空的操作,因为全局块不可能被系统所回收,相当于一个单例。
_NSConcreteStackBlock:栈上的block,在函数返回时会销毁,但是给栈block发送copy消息,就可以把block从栈上拷贝到堆上去了,一旦拷贝到堆上,block就成了带引用计数的对象了,而后续的拷贝操作都不会真的执行,只是递增block的引用计数罢了。
_NSConcreteMallocBlock:堆上的block,引用计数为0时销毁。
ARC下的block
apple官方文档中有说明,在ARC模式下,在栈间传递block时,不需要手动copy栈中的block,即可让NSConcreteStackBlock 的 block 被 NSConcreteMallocBlock 类型的 block 替代,因为ARC会自动执行copy操作。
参考文献
谈Objective-C block的实现Block技巧与底层解析
Objective-C中的Block
相关文章推荐
- 继承构造函数
- 关于博主
- TeamTalk源码分析之login server
- Windows Sysinternals Suite_2016.05.07
- jslint
- awk使用实例详解
- android框架
- AppiumDriver升级到2.0.0版本引发的问题--Cannot instantiate the type AppiumDriver
- windows中squid更改默认安装路径配置说明
- 【转载】基于AFNetWorking3.0的图片缓存分析
- Android 广播机制
- 自己实现js运动框架的一些心得
- 查看和删除镜像
- android.view.InflateException: Binary XML file line
- swap 函数的编写
- java学习路线
- eventBus学习
- IntelliJ IDEA之项目热部署设置
- 集中设备采集监控系统架构思路
- ASP.NET MVC中给所有的cshtml页面引用命名空间