6

Trong tiêu đề thời gian chạy cấp thấp của Mục tiêu-C (/usr/include/objc), có tệp objc-exceptions.h. Có vẻ như đây là cách @try/@catch được trình biên dịch ObjC thực hiện.Ví dụ về cách thực hiện @ try- @ catch của Objective-C được thực thi khi chạy?

Tôi đang cố gắng gọi các hàm này theo cách thủ công (đối với các thử nghiệm có thời gian chạy và triển khai ObjC) để bắt được ngoại lệ "không được nhận dạng được gửi tới lớp". Vì vậy, về cơ bản, tất cả những gì tôi đang tìm kiếm là một ví dụ về cách thực hiện một số @try/@catch bằng cách sử dụng các hàm thời gian chạy ở mức độ thấp. Cảm ơn trước!

+0

Xem xét obj-C++, bạn có muốn thực hiện việc tự giải phóng không? – hamstergene

+0

Eugene, tôi không chắc chắn tôi làm theo ... Tôi đang tìm một ví dụ loại giống như một uncaughtException tôi đã có thể tìm ra ở đây: https://gist.github.com/1073294#file_uncaught_exception. m, nhưng tôi đoán cái gì đó giống như một loại 'begin_try' và' end_try'. – TooTallNate

+0

Việc lắp ráp khối try/catch thực sự hiển thị các cuộc gọi đến objc_begin_catch và objc_end_catch. Bạn đã thử xem qua đó để xem chúng được viện dẫn như thế nào? –

Trả lời

7

Vì vậy, bạn muốn biết thời gian chạy xử lý ngoại lệ như thế nào?

Chuẩn bị thất vọng.

Vì không. ObjC không có ABI xử lý ngoại lệ, chỉ có SPI mà bạn đã tìm thấy. Không nghi ngờ gì nữa, bạn cũng đã phát hiện ra rằng ABI Objective-C ngoại lệ thực sự là chính xác như là C++ exception handling ABI. Để kết thúc, hãy bắt đầu với một số mã.

#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"); 
    } 
} 

Chạy qua kêu vang với -ObjC -O3 (và bị tước một lượng kinh tởm của thông tin debug), chúng tôi có được điều này:

_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 + [email protected]] 

    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 

Nếu bạn biên dịch nó với ObjC++ có gì thay đổi. (Vâng, đó không phải là hoàn toàn đúng sự thật. _objc_terminate cuối cùng biến thành một bước nhảy vào thói quen cá nhân ___clang_call_terminate của clang). Nhưng dù sao, mã này có thể được chia thành 3 phần quan trọng. Đầu tiên là từ _main đến đầu số LBB0_2 hoặc nơi khối thử của chúng tôi diễn ra. Bởi vì chúng tôi đang blatantly ném một ngoại lệ và bắt nó trong khối try của chúng tôi, trình biên dịch đã đi trước và loại bỏ các chi nhánh xung quanh LBB0_2 và di chuyển thẳng đến xử lý bắt. Tại thời điểm này Objective-C, hoặc CoreFoundation chính xác hơn, đã thiết lập một đối tượng ngoại lệ cho chúng ta và libC++ đã bắt đầu tìm kiếm một trình xử lý ngoại lệ trong giai đoạn thư giãn cần thiết.

Khối mã quan trọng thứ hai là từ LBB0_2 đến cuối LBB0_11 nơi các khối catchfinally của chúng tôi hoạt động. Bởi vì tất cả là tốt, tất cả các mã dưới đây là chết (và hy vọng bị tước trong phát hành), nhưng hãy tưởng tượng nó không phải là.

Phần thứ ba là từ LBB0_8 trên xuống nơi trình biên dịch sẽ phát ra một bước nhảy từ NSLog trong LBB0_2 nếu chúng tôi đã làm điều gì đó ngu ngốc như, nói, cố gắng không để bắt ngoại lệ của chúng tôi. Trình xử lý này thay vì lật một chút sau khi gọi vào số objc_begin_catch khiến chúng tôi rẽ nhánh trên ret và di chuyển lên objc_exception_rethrow() cho trình xử lý thư giãn mà chúng tôi đã loại bỏ bóng và tiếp tục tìm kiếm trình xử lý ở một nơi khác. Tất nhiên, chúng tôi là chính, vì vậy không có xử lý khác, và std::terminate được gọi khi chúng tôi rời khỏi.

Tất cả điều này để nói rằng bạn sẽ có một khoảng thời gian tồi tệ nếu bạn muốn thử viết nội dung này bằng tay. Tất cả các hàm __cxa_* và ObjC SPI ném xung quanh các đối tượng ngoại lệ theo cách bạn không thể dựa vào và (nhiều bi quan) xử lý được phát ra trong một very tight order để đảm bảo hợp đồng C++ ABI được đáp ứng bởi vì nếu nó không phải là yêu cầu cụ thể std::terminate được gọi là.Nếu bạn muốn tham gia vai trò lắng nghe tích cực, bạn được phép redefine the exception handling stuff với các chức năng của riêng bạn và Mục tiêu-C có objc_setUncaughtExceptionHandler, objc_setExceptionMatcherobjc_setExceptionPreprocessor.

Các vấn đề liên quan