第37条 理解“块”这一概念

块的强大之处是:在声明它的范围里,所有变量都可以为其所捕获。这就是说,那个范围里的全部变量,在块里依然可用。比如:
int additional = 5;
int (^addBlock)(int a, int b) = ^(int a, int b){
return a + b + additional;
};
默认的情况下,为块所捕获的变量,是不可以在块里修改的。不过,声明变量的时候可以加上__block修饰符。这样就可以在块内修改了。

如果块所捕获的变量是对象类型,那么就会自动保留它。系统在释放这个块的时候,也会将其一并释放。这就引出了一个与块有关的重要问题。块本身可视为对象。实际上,在其他Objective-C 对象所能响应的选择子中,有很多块也是可以响应的。而最重要之处则在于,块本身也和其他对象一样,有引用计数。当最后一个指向块的引用移走之后,块就回收了。
回收时也会释放块所捕获的变量。以便平衡捕获时所执行的保留操作。

如果将块定义在Objective-C类的实例方法中,那么除了可以访问类的所有实例变量之外,还可以使用self变量。块总能修改实例变量,所以在声明时,无须加__block.不过,如果通过读取或写入操作捕获了实例变量,那么也会自动把self变量一并捕获了,因为实例变量是与self所指代的实例变量关联在一起的。

全局块/栈块/堆块

定义块的时候,其所占的内存区域是分配在栈中的。这就是说,块只在定义它的哪个范围内有效。
void (^block)();
if ( ...)
{
block = ^{
NSLog(@"Block A");
};
}
else
{
block = ^{
NSLog(@"Block B");
};
}

block();

定义在if以及else 语句中的两个块都分配在栈内存中。这两个块只能保证在对应的if 或else 语句范围内有效。

为解决此问题,可以给块对象发送copy消息以拷贝之。这样的话,就可以把块从栈复制到堆了。拷贝后的块,就可以在定义它的哪个范围之外使用。而且,一旦复制到堆上,块就可以成了带引用计数的对象了。后续的复制操作都不会真的执行复制,只是递增块对象的引用计数。
如果不再使用这个块,那就应将其释放。当引用计数降为0后,分配在堆上的块会像其他对象一样,为系统所回收。而分配在栈上的块则无须明确释放,因为栈内存本来就会自动回收。

你可能感兴趣的:(第37条 理解“块”这一概念)