typedef int (^blk_t)(int);
blk_t func ( int rate )
{
return ^(int count){ return rate*count; };
}
以上返回配置在栈上的Block的函数,虽然当作用域废弃后,栈上的Block也会被废弃。但是通过对应的ARC的编译器可转为如下:
blk_t func (int rate)
{
// 生成处于栈上的Block
blk_t tmp = &__func_block_impl_0 (__func_block_func_0, &__func_block_desc_0DATA, rate);
// 复制到堆上
tmp = objc_retainBlock(tmp);
// 注册到 autoreleasepool 中,然后返回该对象
return objc_autoreleaseReturnValue(tmp);
}
因为ARC处于有效的状态,blk_t tmp 实际上与附有__strong修饰符的 blk_t __strong tmp 相同。
而 objc_retainBlock 函数实际上就是 Block_copy 函数。
① 向方法或者函数的参数中传递 Block 时需要手动实现 copy 方法。
以下情况不必手动复制,因为该函数中已经适当的复制了传递过来的参数:
① Cocoa 框架的方法且方法名中含有 usingBlock 等时;
② Grand Central Dispatch 的API;
例如: 使用NSArray 类的 enumerateObjectUsingBlock 实例方法以及 dispatch_async 函数时,不用手动复制;
- (id)getBlockArray
{
int val = 10;
return [[NSArray alloc] initWithObjcets:
^{NSLog(@“blk0:%d” ,val) ;},
^{NSLog(@“blk1:%d”,val) ;}, nil];
}
id obj getBlockArray ();
typedef void (^blk_t)(void);
blk_t blk = (blk_t)[obj objectAtIndex:0];
blk();
以上代码运行时会发生异常。这是由于在getBlockArray 函数结束后,Block被废弃的缘故。
将Block 从栈上复制到堆上比较消耗cpu资源
- (id)getBlockArray
{
int val = 10;
return [[NSArray alloc] initWithObjcets:
[^{NSLog(@“blk0:%d” ,val) ;} copy] ,
[^{NSLog(@“blk1:%d”,val) ;} copy] , nil];
}
对于已配置在堆上的Block以及配置在程序的数据区域上的Block,调用copy方法又会如何?
类 |
设置对象的存储域 |
复制效果 |
_NSConcreteStackBlock |
栈 |
从栈复制到堆 |
_NSConcreteGlobalBlock |
程序的数据区域(.data区) |
什么也不做 |
_NSConcreteMallocBlock |
堆 |
引用计数增加 |
多次调用 copy 是否有问题:
blk = [[[[blk copy] copy] copy] copy];
可解释为:
{
blk_t tmp = [blk copy];
blk = tmp;
}
{
blk_t tmp = [blk copy];
blk = tmp;
}
{
blk_t tmp = [blk copy];
blk = tmp;
}
{
blk_t tmp = [blk copy];
blk = tmp;
}
代码解析:
{
// 将配置在栈上的Block 赋值给变量blk中
blk_t tmp = [blk copy];
// 将配置在堆上的Block赋值给变量tmp中,变量tmp持有强引用的Block。
blk = tmp;
// 将变量tmp 的Block 赋值为变量blk,变量blk 持有强引用的Block。
// 此时Block 的持有者有 blk 和 tmp
}
// 由于变量作用域结束,所以变量tmp 被废弃,其强引用失效。
// 由于Block 还被blk 持有,所有被废弃
Block 从栈复制到堆时堆 __block变量产生的影响
__block 变量的配置存储域 |
Block 从栈复制到堆 的影响 |
栈 |
从栈复制到堆并被Block持有 |
堆 |
被Block持有 |
当多个Block中使用__block变量时,在任何一个Block从栈复制到堆的时候,__block变量也会一并从栈复制到堆并被该Block所持有。
当剩下的Block从栈复制到堆时,被复制的Block持有 __block 变量,并增加唉 __block 变量的引用计数。
故而 __block 变量中使用 __forwarding 指针变量就是为了无论在栈还是在堆,都能正确访问到该变量。