NO
第六章 块与大中枢派发(GCD)
37. 块的基本概念
- 块的强大之处:在声明它的范围里所有变量都可以为其所捕获. 默认情况下,为块所捕获的变量,是不可以在块里修改的.若声明变量的时候加上__block修饰符,就可以在块内修改了.
- 块可以分配在栈或堆上,�也可以是全局的.分配在栈上的块可以copy到堆上,一旦拷贝到堆上,块就成了带引用计数的对象了.后续的复制都不会真的执行复制,只是递增块对象的引用计数
- 全局块:不会捕捉任何状态,运行时也无须有状态来参与.块所使用的整个内存在编译期就已经完全确定了.属于一种优化操作
void (^block)() = ^{
NSLog(@"全局块");
};
- 如果将块定义在实例方法中,那么除了可以访问类的所有实例变量外,还可以使用self变量.块总能修改实例变量,所以在声明时无需添加__block.不过如果通过读取或写入操作捕获了实例变量,那么也会自动将self变量一并捕获,因为实例变量是与self所指代的实例关联在一起的.
- self也是个对象,因而块在捕获它时,也会将其保留,如果self所指代的对象同时也保留了块,那么会导致保留环.
块的内部结构
-
块的内存布局:块本身也是个对象,在存放的内存区域中,首个变量是指向Class对象的指针.其余内存里含有块对象正常运转所需的各种信息
- 在内存布局中,最重要的就是invoke变量,这是个函数指针,指向块的实现代码.函数原型至少要接受一个void* 型的参数,此参数代表块. 块其实就是一种代替函数指针的语法结构,原来使用函数指针时,需要用"不透明的void指针"来传递状态,而改用块后则可以把原来用C语言所编写的代码封装成简明易用的接口.
- descriptor变量是指向结构体的指针,每个块里都包含此结构体,其中声明了块对象的总体大小,还声明了copy和dispose两个辅助函数所对应的函数指针.辅助函数在拷贝及丢弃块对象是运行,比如copy要保留捕获的对象,dispose则将之释放.
- 块还会把它所捕获的所有变量都拷贝一份.这些拷贝放在descriptor变量后面,捕获多少个变量,就要占据多少内存空间.请注意拷贝的不是对象本身,而是指向这些对象的指针变量.