18

我想从内部递归调用一个块。在 obj-c 对象中,我们可以使用“self”,是否有类似的东西可以从自身内部引用块实例?

4

4 回答 4

27

有趣的故事!块实际上是 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,否则,block_self内部的引用fibonacci将在分配之前引用它(在第一次递归调用时使您的程序崩溃)。这__weak是为了确保块不会捕获对自身的强引用,这会导致内存泄漏。

于 2011-01-28T03:43:04.040 回答
14

以下递归块代码将使用 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);
                   });
}

重要注意事项:

  • 您必须手动将块复制到堆上,否则当您从另一个上下文调用它时它会尝试访问不存在的堆栈(ARC 通常会为您执行此操作,但并非在所有情况下都可以安全地使用它)。
  • 您需要两个引用:一个保存对块的强引用,另一个保存要调用的递归块的弱引用(从技术上讲,这只需要 ARC)。
  • 您必须使用 __block 限定符,以便块不会捕获块引用的尚未分配的值。
  • 如果您正在执行手动内存管理,则需要自己自动释放复制的块。
于 2012-05-12T03:06:54.710 回答
6

您必须将块变量声明为__block

typedef void (^MyBlock)(id);

__block MyBlock block = ^(id param) {
  NSLog(@"%@", param);
  block(param);
};
于 2011-01-28T03:43:46.650 回答
3

没有selffor 块(还)。您可以像这样构建一个(假设为 ARC):

__block void (__weak ^blockSelf)(void);
void (^block)(void) = [^{
        // Use blockSelf here
} copy];
blockSelf = block;
    // Use block here

__block是必需的,因此我们可以blockSelf在创建块后设置块。这__weak是必需的,因为否则该块将持有对自身的强引用,这将导致强引用循环并因此导致内存泄漏。copy需要确保将块复制到堆中。对于较新的编译器版本,这可能是不必要的,但它不会造成任何伤害。

于 2013-06-21T12:33:53.530 回答