ios - dispatch_async 是否复制内部 block

标签 ios objective-c objective-c-blocks reference-counting

给定以下(手动引用计数):

void (^block)(void) = ^ {
    NSLog(@"wuttup");
}

void (^async_block)(void) = ^ {
    block();
}

dispatch_async(dispatch_get_main_queue(), async_block);

“ block ”会被复制而不是从堆栈中扔掉并销毁吗?

最佳答案

我相信,答案是肯定的。

外部 block 将被异步调度,这会导致运行时在堆上为该 block 制作一个副本。如下所示,并在 Block Implementation Specification - Clang 3.4 Documentation 中进行了描述,内部 block 的导入变量也被复制到堆中。

在 OP 的示例中,我们有一个“ block 引用的导入常量副本”。

我正在使用规范中的示例:

void (^existingBlock)(void) = ...;
void (^vv)(void) = ^{ existingBlock(); }
vv();

规范指出需要 copy_helperdispose_helper 函数:

The copy_helper function is passed both the existing stack based pointer and the pointer to the new heap version and should call back into the runtime to actually do the copy operation on the imported fields within the Block.

规范中的以下示例代码很难破译(实际上缺少描述将外部 block 复制到堆时发生的情况)。无论如何,规范似乎试图表明内部 block 的导入变量将(递归地)复制到外部 block 的原始存储区域。

当外部 block 将被复制到堆上时,似乎内部 block 的导入变量最终也会存在于堆上。

嗯,直觉上,这一切都是有道理的。

我制作了一个小测试程序来演示这一点: (您必须调试和检查反汇编才能弄清楚表面下发生了什么)。

#import <Foundation/Foundation.h>


void foo(int param)
{
    int x0 = param;
    int x1 = param + 1;
    void (^existingBlock)(void) = ^{
        int y0 = x0;
        int y1 = x1;
        printf("&y0: %p\n", &y0);
        printf("&y1: %p\n", &y1);
        printf("&x0: %p\n", &x0);
        printf("&x1: %p\n", &x1);
    };

    void (^vv)(void) = ^{
        int y2 = x0;
        int y3 = x1;
        existingBlock();
        printf("&y2: %p\n", &y2);
        printf("&y3: %p\n", &y3);
        printf("&x0: %p\n", &x0);
        printf("&x1: %p\n", &x1);
    };

    printf("Stack: &x: %p\n", &x0);
    printf("Stack: &x: %p\n", &x1);

    printf("------- on main thread -------\n");
    vv();

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        printf("------- on thread 2 -------\n");
        assert(vv);
        sleep(1);
        int y4 = x0;
        int y5 = x1;
        vv();
        printf("&y4: %p\n", &y4);
        printf("&y5: %p\n", &y5);
        printf("&x0: %p\n", &x0);
        printf("&x1: %p\n", &x1);
    });
}

int main(int argc, const char * argv[])
{
    @autoreleasepool {

        foo(1);
        sleep(2);
    }
    return 0;
}

输出如下:

Stack: &x: 0x7fff5fbff868
Stack: &x: 0x7fff5fbff864
------- on main thread -------
&y0: 0x7fff5fbff70c
&y1: 0x7fff5fbff708
&x0: 0x1001081e0
&x1: 0x1001081e4
&y2: 0x7fff5fbff76c
&y3: 0x7fff5fbff768
&x0: 0x10010a588
&x1: 0x10010a58c
------- on thread 2 -------
&y0: 0x1000e5d9c
&y1: 0x1000e5d98
&x0: 0x1001081e0
&x1: 0x1001081e4
&y2: 0x1000e5dfc
&y3: 0x1000e5df8
&x0: 0x10010a588
&x1: 0x10010a58c
&y4: 0x1000e5e6c
&y5: 0x1000e5e68
&x0: 0x10010a5e8
&x1: 0x10010a5ec

当 block 在主线程上执行时,它位于堆栈中(如本地变量和导入变量的地址所示)。当通过 dispatch_async 执行时,运行时复制了 block - 包括内部 block ,从 block 的本地变量和导入变量的地址可以看出。

我们可以在copy_helper_block函数处设置一个断点,实际上,程序会停在那里一次,以便将 block vv复制到堆中。

enter image description here

关于ios - dispatch_async 是否复制内部 block ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18157360/

相关文章:

ios - 没有 "KeyboardName - HostAppName"方案的自定义键盘名称

ios - 如何使用 facebook 从 ios 模拟器登录?

javascript - Cordova - 操作系统升级后应用程序在 iOS/Android 上崩溃

objective-c - 为 hello world 启动什么样的项目重要吗?

objective-c - 通过dispatch_async block 传递可变参数

ios - "unrecognized selector sent to instance"属性 setter

objective-c - 这些继承检查有什么区别?

ios - 格式化 URL 以在 Facebook 帖子中显示为字符串

iPhone 应用程序崩溃并出现错误 [UIApplication _cachedSystemAnimationFenceCreatingIfNecessary :]

ios - block 内的强弱。我该如何处理这个问题?