6

Objective-Cの低レベルのランタイムヘッダー(/usr/include/objc)には、objc-exceptions.hファイルがあります。これは、ObjCコンパイラによって@try/がどのように実装されているかと思われます。@catch

「クラスに送信された認識されないセレクター」例外をキャッチするために、これらの関数を手動で呼び出そうとしています(ObjCランタイムと実装の実験用)。

したがって、基本的に、私が探しているのは、低レベルのランタイム関数を使用して@try/を実行する方法の例です。@catch前もって感謝します!

4

1 に答える 1

7

では、ランタイムがどのように例外処理を行うのか知りたいですか?

がっかりする準備をしなさい。

そうではないからです。ObjCには例外処理ABIはなく、既に見つけたSPIのみがあります。また、Objective-C例外ABIが、実際にはABIを処理するC++例外とまったく同じであることも発見したことは間違いありません。そのために、いくつかのコードから始めましょう。

#include <Foundation/Foundation.h>

int main(int argc, char **argv) {
    @try {
        @throw [NSException exceptionWithName:@"ExceptionalCircumstances" reason:@"Drunk on power" userInfo:nil];
    } @catch(...) {
        NSLog(@"Catch");
    } @finally {
        NSLog(@"Finally");
    }
}

clangを実行して-ObjC -O3(そして嫌な量のデバッグ情報を取り除いて)これを取得します:

_main:                                  ## @main
    push    rbp
    mov rbp, rsp
    push    r14
    push    rbx

    mov rdi, qword ptr [rip + L_OBJC_CLASSLIST_REFERENCES_$_]
    mov rsi, qword ptr [rip + L_OBJC_SELECTOR_REFERENCES_]

    lea rdx, qword ptr [rip + L__unnamed_cfstring_]
    lea rcx, qword ptr [rip + L__unnamed_cfstring_2]
    xor r8d, r8d
    call    qword ptr [rip + _objc_msgSend@GOTPCREL]

    mov rdi, rax
    call    _objc_exception_throw
LBB0_2:
    mov rdi, rax
    call    _objc_begin_catch

    lea rdi, qword ptr [rip + L__unnamed_cfstring_4]
    xor eax, eax
    call    _NSLog

    call    _objc_end_catch

    xor ebx, ebx
LBB0_8:
    lea rdi, qword ptr [rip + L__unnamed_cfstring_6]
    xor eax, eax
    call    _NSLog

    test    bl, bl
    jne LBB0_10
LBB0_11:
    xor eax, eax
    pop rbx
    pop r14
    pop rbp
    ret
LBB0_5:
    mov rbx, rax
    call    _objc_end_catch
    jmp LBB0_7
LBB0_6:
    mov rbx, rax
LBB0_7:
    mov rdi, rbx
    call    _objc_begin_catch
    mov bl, 1
    jmp LBB0_8
LBB0_12:
    mov r14, rax
    test    bl, bl
    je  LBB0_14
    jmp LBB0_13
LBB0_10:
    call    _objc_exception_rethrow
    jmp LBB0_11
LBB0_16:                                ## %.thread
    mov r14, rax
LBB0_13:
    call    _objc_end_catch
LBB0_14:
    mov rdi, r14
    call    __Unwind_Resume
LBB0_15:
    call    _objc_terminate

ObjC ++でコンパイルしても、何も変わりません。(まあ、それは完全に真実ではありません。最後はclangの個人的なルーチン_objc_terminateへのジャンプに変わります)。___clang_call_terminateとにかく、このコードは3つの重要なセクションに分けることができます。1つ目は_main、の開始から開始までLBB0_2、またはtryブロックが発生する場所です。露骨に例外をスローしてtryブロックでキャッチしているため、コンパイラーは先に進んでブランチを削除LBB0_2し、catchハンドラーに直接移動しました。この時点で、Objective-C、より正確にはCoreFoundationが例外オブジェクトを設定し、libC++は必要な巻き戻しフェーズ中に例外ハンドラーの検索を開始しました。

コードの2番目の重要なブロックは、私たちとブロックが存在する場所LBB0_2の終わりまでです。すべてが順調であるため、この下のすべてのコードは死んでいます(そして、うまくいけばリリースで削除されます)が、そうではなかったと想像してみましょう。 LBB0_11catchfinally

3番目の部分は、例外をキャッチしないようにしたなどの愚かなことをLBB0_8した場合に、コンパイラがNSLoginからジャンプを発行する場所です。代わりに、このハンドラーは、をLBB0_2呼び出した後に少し反転し、ボールをドロップしたことをアンワインドハンドラーに通知し、別の場所でハンドラーの検索を続行するように、に移動します。もちろん、私たちはメインなので、他のハンドラーはなく、離れるときに呼び出されます。objc_begin_catchretobjc_exception_rethrow()std::terminate

このようなものを手で書き出そうとすると、悪い時間を過ごすことになると言っても過言ではありません。すべての__cxa_*およびObjCSPI関数は、信頼できない方法で例外オブジェクトをスローし、(悲観的には多くの)ハンドラーが非常にタイトな順序で発行され、C++ABIコントラクトが確実に満たされるようにします。std::terminate呼ばれる。アクティブなリスニングの役割を引き受けたい場合は、独自の関数を使用して例外処理を再定義することができます。Objective-Cにはobjc_setUncaughtExceptionHandler、がありobjc_setExceptionMatcher objc_setExceptionPreprocessorます。

于 2015-02-08T05:47:51.883 に答える