Blocks是带有自动变量(局部变量) 的匿名函数
语法
1 | ^int (int val) { |
可省略参数列表以及返回值
1 | // 自动推导返回值类型为int |
Blocks变量的声明:将C语言函数指针声明中的*
改成^
1 | // 变量名字为blk |
通过typedef
简化函数定义
1 | typedef int (^blk_t)(int); |
变量的捕获
Block表达式截获定义时自动变量的值,值只读
1 | int val = 10; |
添加__block
声明,值可写
实现原理
例子
1 | int main() { |
上面的Block由几个部分组成
- 函数体 将Block内的函数体放到一个静态函数中
1
2
3static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
printf("Block\n");
} - 定制Block结构体 分为以下两个部分
1
2
3
4
5struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0 *Desc;
// 截获的变量...
}- 常规Block结构体 通用的Block结构,如
1
2
3
4
5
6struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr; // 保存函数体指针
};isa
,函数指针等 - 定制Block描述块 特定的属于该Block的描述结构,如
1
2
3
4struct __main_block_desc_0 {
unsigned long reserved;
unsigned long Block_size;
} - Block描述块数据
1
2
3static __main_block_desc_0 __main_block_desc_0_DATA = {
0, sizeof(struct __main_block_impl_0)
}; - 截获的变量
各种类型变量的存储情况:- 对于局部变量,值存储
- 对于全局变量,不进行存储
- 对于静态局部变量,存储指针
- 常规Block结构体
转换后的实现
1 | struct __main_block_impl_0 blk = |
__block
实现
例子
1 | int main() { |
__block int val = 10
转换为
1 | struct __Block_byref_val_0 { |
^{val = 1;}
转换为
1 | static void __main_block_func_0(struct __main_block_impl_0 *__cself) { |
本质上也是保存对象的指针,但是与静态局部变量不一样的是,这个指针并非存到__main_block_impl_0
中,而是存放到__Block_byref_val_0
中,这样目的是一个__block
变量能被多个Block使用
Block存储域
- 栈:
isa
为_NSConcreteStackBlock
- 堆:
isa
为_NSConcreteMallocBlock
- .data:
isa
为_NSConcreteGlobalBlock
一般Block都存在栈上,存在.data段上的两种情况为:
- Block在全局变量中声明
- Block语法表达式中不截获自动变量
1
2
3
4
5
6
7typedef int (^blk_t)(int);
for (int rate = 0; rate < 10; ++rate) {
// _NSConcreteStackBlock
blk_t blk = ^(int count) { return rate*count; }
// _NSConcreteGlobalBlock
blk_t blk2 = ^(int count) { return count; }
}
存在堆上面的情况为:
变量作用域结束
1 | blk_t func(int rate) { |
函数返回一个Block,Block在栈上面生成的。当ARC有效时,编译器会自动生成从栈上复制到堆上的代码
1 | blk_t func(int rate) { |
copy
使用[blk copy]
的效果:
_NSConcreteStackBlock
:从栈复制到堆_NSConcreteMallocBlock
:引用计数增加_NSConcreteGlobalBlock
:什么也不做
__block
变量存储域
__block
变量由使用它的Block管理__block
变量正确访问的原理:
使用__forwarding
成员变量。当__block
变量发生复制行为时,正确修改__forwarding
成员变量
截获对象
当截获的变量是对象时,存放到__main_block_impl_0
时需要用__strong
修饰
1 | struct __main_block_impl_0 { |
除此之外,还有一对copy
/dispose
函数,用于管理对象的引用计数。其中copy
在Block从栈上复制到堆上调用;dispose
在Block在堆上被废弃时调用
1 | static void __main_block_copy_0( |
Block从栈上复制到堆上的情况:
[blk copy]
- Block作为函数返回值返回时
- Block赋值给
__strong
修饰符id类型的类或者Block类型成员变脸时
__block
对象
同样,存到__Block_byref_val_0
时需要用__strong
修饰
1 | struct __Block_byref_val_0 { |
同样,也生成一对copy
/dispose
静态函数,作用相同