【发布时间】:2011-03-05 02:38:33
【问题描述】:
self 只是块内的捕获变量,并不引用块本身,那么块如何在没有明确捕获变量的情况下引用自身呢?
【问题讨论】:
标签: objective-c objective-c-blocks
self 只是块内的捕获变量,并不引用块本身,那么块如何在没有明确捕获变量的情况下引用自身呢?
【问题讨论】:
标签: objective-c objective-c-blocks
__block void(^strawberryFields)();
strawberryFields = [^{ strawberryFields(); } copy];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),
strawberryFields);
您使用__block,因为该块将在创建块时复制strawberryFields 的值,这将在分配之前。
您还必须在任何其他复制操作之前copy 块,否则您最终会得到一个引用堆栈原始版本的块。
请注意,上面的代码泄露了区块。在某个地方,需要该块的release 来平衡副本。
【讨论】:
dispatch_async之后?
我发现这种模式对于 ARC(自动引用计数)在 Debug 和 Release 版本中都有效且稳定。
-(void) someMethod
{
// declare a __block variable to use inside the block itself for its recursive phase.
void __block (^myBlock_recurse)();
// define the block
void (^myBlock)() = ^{
// ... do stuff ...
myBlock_recurse(); // looks like calling another block, but not really.
};
// kickstart the block
myBlock_recurse = myBlock; // initialize the alias
myBlock(); // starts the block
}
最初我尝试将__block 修饰符添加到myBlock 并直接使用该变量在块的实现中递归。这适用于 ARC Debug 版本,但在 Release 版本中与 EXC_BAD_ACCESS 中断。另一方面,删除 __block 修饰符会引发“在被块捕获时未定义变量”警告(我不愿意运行它并进行测试)。
【讨论】:
myBlock 时会崩溃,但在使用 myBlock_recurse 时不会崩溃?
nil 分配给 myBlock_recurse。
我以前从未尝试过,也不是 100% 确定它是否有用,如果有效,但例如:
typedef void (^BasicBlock)(void);
__block BasicBlock testBlock;
testBlock = ^{NSLog(@"Testing %p", &testBlock);};
testBlock();
您可能已经使用 __block 声明变量以防止自保留循环。
【讨论】:
__block 变量的地址通常令人惊讶(因为存在隐含的间接)。
__block 变量实际上是一个句柄;指向数据实际保存位置的指针。它从堆栈开始。当您执行 Block_copy()(或 -copy)时,它会在堆上分配一点内存来保存变量值,复制它,然后更新 __block 中的指针以不再指向堆栈。
块需要一些方法来消除它自己的引用。通常是通过将块存储在类的属性中来完成的。
有时您可能宁愿不使用属性。以下是没有属性的情况:
__weak id weakSelf = self;
__block id block = ^{
if(weakSelf) {
// .. do whatever
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC), dispatch_get_main_queue(), block);
}
else {
block = nil;
}
};
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC), dispatch_get_main_queue(), block);
要记住的关键是所有代码路径都必须指向 block = nil。我们在这里每 5 秒调用一次块,直到 weakSelf 变为 nil。
【讨论】:
请注意,在 ARC 中,它有点不同——__block 对象指针变量默认保留在 ARC 中,这与 MRC 不同。因此,它将导致保留循环。块必须捕获对自身的弱引用(使用__weak)才能没有保留周期。
但是,我们仍然需要在某处对块进行强引用。如果没有强引用,则块(因为它被复制而在堆上)将被释放。因此,我们需要两个变量,一个是强的,一个是弱的,并且在块内部使用弱的变量来引用自己:
__block __weak void(^weakBlock)();
void(^myBlock)();
weakBlock = myBlock = [^{ weakBlock(); } copy];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),
myBlock);
【讨论】:
copy会怎样?
weakBlock 将指向堆栈块。而且即使我们以后可能会使用块的副本(堆块),在块内部,代码仍然引用weakBlock,它指向堆栈块,因此它会执行堆栈块并导致问题。这就是为什么在分配给weakBlock之前必须复制块的原因。
myBlock,这是一个强引用。赋值表达式计算右侧的结果,并将该结果分配给强引用。编译器对赋值的顺序有错误的挑剔。根据标准,代码是正确的,但我已经更改它以消除警告。