objective-c - 为什么 Clang 在没有 return 语句的 block 中被 @try{} 混淆了?

标签 objective-c clang objective-c-blocks

在正常情况下,当一个 block 被声明为返回一个值,但没有 return 语句实际出现在 block 中时,Clang 将无法编译它并出现错误(缺少返回值)。

但是,当该 block 包含 @try{} @catch(...){}@try{} @finally{} 时,这会中断。

有人知道为什么吗?

我发现这一点的方法是在 ReactiveCocoa 的 RACExtScope 中使用 @weakify()@strongify() 时,在一个 block 中我忘记返回信号。但是编译器没有警告我并在运行时崩溃,这导致我深入研究它,预处理代码并发现这是导致它的原因。任何解释将不胜感激,老实说我不知道​​为什么会这样,谢谢!

我还创建了一个要点,以防有人有评论/建议:https://gist.github.com/czechboy0/11358741

int main(int argc, const char * argv[])
{
    id (^iReturnStuff)() = ^id() {
        @try{} @finally{}
        //if you comment out line 4, Clang will not compile this.
        //if you leave it like this, Clang will compile and run this, even though
        //there's no value being returned.
        //is there something special in @try{} that turns off compiler errors?
    };
    return 0;
}

最佳答案

Clang's block specification简要提及 block 中的控制流。我在这里复制了它(强调我的)

The compound statement of a Block is treated much like a function body with respect to control flow in that goto, break, and continue do not escape the Block. Exceptions are treated normally in that when thrown they pop stack frames until a catch clause is found.

进一步阅读,您会真正感觉到 Objective-C 中的异常是彻头彻尾的怪异。来自 the section on exceptions

The standard Cocoa convention is that exceptions signal programmer error and are not intended to be recovered from. Making code exceptions-safe by default would impose severe runtime and code size penalties on code that typically does not actually care about exceptions safety. Therefore, ARC-generated code leaks by default on exceptions, which is just fine if the process is going to be immediately terminated anyway. Programs which do care about recovering from exceptions should enable the option.

从上面可以合理地推断出 ObjC 异常规范是如此脆弱或可塑,以至于编译器编写者都不能保证针对它的稳定代码,因此一旦 @try-@catch 被禁用,他们就禁用所有合理的终止检查遭遇。

这也可以在 Clang 生成的带有和不带有 try-catch 的代码中看到。一、无

___main_block_invoke:
    pushq   %rbp
    movq    %rsp, %rbp
    movabsq $0, %rax
    movq    %rdi, -8(%rbp)
    movq    %rdi, -16(%rbp)
    popq    %rbp
    ret

这是非常简单的 x86,它压入一个新的堆栈帧,将 0 (nil) 移动到返回寄存器,然后返回。现在,使用 try-catch block :

___main_block_invoke:
    pushq   %rbp
    movq    %rsp, %rbp
    subq    $64, %rsp
    movq    %rdi, -16(%rbp)
    movq    %rdi, -24(%rbp)
    movb    $0, -25(%rbp)
    movl    -32(%rbp), %eax
    testb   $1, -25(%rbp)
    movl    %eax, -48(%rbp)         ## 4-byte Spill
    jne LBB1_1
    jmp LBB1_3
    LBB1_1:
    callq   _objc_exception_rethrow
    jmp LBB1_2
    LBB1_2:
    LBB1_3:
    movl    -48(%rbp), %eax         ## 4-byte Reload
    movl    %eax, -32(%rbp)
    movq    -8(%rbp), %rdi
    addq    $64, %rsp
    popq    %rbp
    jmp _objc_autoreleaseReturnValue ## TAILCALL
    LBB1_4:
    movl    %edx, %ecx
    movq    %rax, -40(%rbp)
    movl    %ecx, -44(%rbp)
    testb   $1, -25(%rbp)
    jne LBB1_5
    jmp LBB1_7
    LBB1_5:
    callq   _objc_end_catch
    jmp LBB1_6
    LBB1_6:
    jmp LBB1_7
    LBB1_7:
    jmp LBB1_8
    LBB1_8:
    movq    -40(%rbp), %rdi
    callq   __Unwind_Resume
    LBB1_9:
    movq    %rdx, -56(%rbp)         ## 8-byte Spill
    movq    %rax, -64(%rbp)         ## 8-byte Spill
    callq   _objc_terminate

除了更复杂的函数 proem,请注意缺少适当的 ret。该函数仍然有两个退出点,

jmp   _objc_autoreleaseReturnValue

call  _objc_terminate

第一个是该语言的一个相对较新的特性,当处于尾调用位置时,它可以用于省略 -autoreleases 以通过检查代码来利用线程局部变量来到它之前。第二个开始立即终止进程并跳转到 C++ 异常处理机制。这意味着该函数实际上确实具有必要的退出点,以防止 CLANG 提示缺少返回语句。不幸的是,这也意味着 CLANG 放弃扰乱 ObjC 异常机制可能会产生垃圾消息,如您所见。这是 EXTScope 拥有 switched to using the @autoreleasepool directive 的原因之一吃那个印记。

关于objective-c - 为什么 Clang 在没有 return 语句的 block 中被 @try{} 混淆了?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23330884/

相关文章:

iOS:离线制作一个http请求队列

ios - Youtube 与 iOS 应用程序集成 - 意外错误

objective-c - 为什么使用 NSDate 将 double 转换为字符串会额外增加 30 分钟

c - 为什么 GCC 不在文件末尾生成任何关于换行符的警告?

c - 严格别名违规 : Why gcc and clang generate different output?

c++ - 是否可以将 C++0x lambda 转换为 clang block ?

objective-c - 如何在十进制和整数之间拆分 NSDecimalnumber 并返回

c++ - Objective-C++ 对 UI 的绝对混淆;最佳实践

objective-c - 重新分配 block objective-c

swift - 以下 block 属性的 swift 属性 block 声明是什么?