【发布时间】:2011-01-28 03:37:24
【问题描述】:
我想从自身内部递归调用一个块。在 obj-c 对象中,我们可以使用“self”,是否有这样的东西可以从自身内部引用块实例?
【问题讨论】:
标签: objective-c cocoa objective-c-blocks
我想从自身内部递归调用一个块。在 obj-c 对象中,我们可以使用“self”,是否有这样的东西可以从自身内部引用块实例?
【问题讨论】:
标签: objective-c cocoa objective-c-blocks
有趣的故事!块实际上是 Objective-C 对象。也就是说,没有公开的 API 来获取块的 self 指针。
但是,如果在使用块之前声明它们,则可以递归地使用它们。在非垃圾收集环境中,您会执行以下操作:
__weak __block int (^block_self)(int);
int (^fibonacci)(int) = [^(int n) {
if (n < 2) { return 1; }
return block_self(n - 1) + block_self(n - 2);
} copy];
block_self = fibonacci;
有必要将__block 修饰符应用于block_self,否则,fibonacci 内的block_self 引用会在分配之前引用它(在第一个递归调用)。 __weak 是为了确保块不会捕获对自身的强引用,这会导致内存泄漏。
【讨论】:
dispatch_async()),你还需要复制赋值范围内的块。
以下递归块代码将使用 ARC、GC 或手动内存管理编译和运行,不会崩溃、泄漏或发出警告(分析器或常规):
typedef void (^CountdownBlock)(int currentValue);
- (CountdownBlock) makeRecursiveBlock
{
CountdownBlock aBlock;
__block __unsafe_unretained CountdownBlock aBlock_recursive;
aBlock_recursive = aBlock = [^(int currentValue)
{
if(currentValue >= 0)
{
NSLog(@"Current value = %d", currentValue);
aBlock_recursive(currentValue-1);
}
} copy];
#if !__has_feature(objc_arc)
[aBlock autorelease];
#endif
return aBlock;
}
- (void) callRecursiveBlock
{
CountdownBlock aBlock = [self makeRecursiveBlock];
// You don't need to dispatch; I'm doing this to demonstrate
// calling from beyond the current autorelease pool.
dispatch_async(dispatch_get_main_queue(), ^
{
aBlock(10);
});
}
重要注意事项:
【讨论】:
copy] autorelease] 不只是复制,所以你不必在块内调用 release 呢?对于 ARC 可能会在调用 aBlock(10); 之后执行 aBlock = nil ?
您必须将块变量声明为__block:
typedef void (^MyBlock)(id);
__block MyBlock block = ^(id param) {
NSLog(@"%@", param);
block(param);
};
【讨论】:
__block specifically is a keyword.
区块(目前)还没有self。您可以像这样构建一个(假设为 ARC):
__block void (__weak ^blockSelf)(void);
void (^block)(void) = [^{
// Use blockSelf here
} copy];
blockSelf = block;
// Use block here
__block 是必需的,因此我们可以在创建块后将blockSelf 设置为块。 __weak 是必需的,否则该块将持有对自身的强引用,这将导致强引用循环并因此导致内存泄漏。需要copy 以确保将块复制到堆中。对于较新的编译器版本,这可能是不必要的,但不会造成任何伤害。
【讨论】: