是否有块的SELF指针?

Arl*_*son 18 cocoa objective-c objective-c-blocks

我想以递归方式从内部调用一个块.在一个obj-c对象中,我们使用"self",是否有这样的东西从内部引用块实例?

zne*_*eak 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;
Run Code Online (Sandbox Code Playgroud)

必要__block修饰符应用于block_self,否则,block_self内部fibonacci引用将在分配之前引用它(在第一次递归调用时使程序崩溃).这__weak是为了确保块不会捕获对自身的强引用,这会导致内存泄漏.

  • 如果将它传递给最终将复制它的函数(例如`dispatch_async()`),则还需要在赋值范围内复制块. (3认同)
  • 即使您有终止条件,此代码也会泄漏.添加倒计时变量并通过仪器泄漏检查器运行它以查看.此外,如果您打开了正确的警告,编译器将发出以下警告:"在此块中捕获'myBlock'可能会导致保留周期" (2认同)

Kar*_*arl 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);
                   });
}
Run Code Online (Sandbox Code Playgroud)

重要注意事项

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


Dav*_*ong 6

您必须将块变量声明为__block:

typedef void (^MyBlock)(id);

__block MyBlock block = ^(id param) {
  NSLog(@"%@", param);
  block(param);
};
Run Code Online (Sandbox Code Playgroud)