所在的位置: C++ >> C++发展 >> 初探ObjectiveCC异常

初探ObjectiveCC异常

做家:Cyandev,iOS和MacOS开拓者,当今就任于字节跳动

0x00引子

反常处置是很多高等说话都具备的性格,它能够直接中缀今朝函数并将把持权转交给能够处置反常的函数。不同说话在反常处置的完结上各不类似,本文重要来剖析一下Objective-C和C++这两个说话。

为甚么要把Objective-C和C++放在一同呢?由于它们在完结机制上太像了,更矜重地说,Objective-C的反常处置机制便是借助C++来完结的。而说到Objective-C的反常处置,还须要引出一个题目,便是内存走漏。它形成的因为是甚么?要怎样处置?这边咱们先留个疑难,在文章背面会诠释。

0x10反常处置做了甚么

反常处置,中心不在反常,而在处置。也便是说无论你抛出的是NSException仍是原始表率,这不过一个音信承载的方法,反常处置最关键的是怎样把这个反常传播给能够处置它的人。尚有最重大的一点,在这个经过中把现场“整理洁净”(这也是C++RAII的精华住址)。

思考底下的代码片断:

staticvoidbar(){structtracker{~tracker(){std::coutthis"destroyed"std::endl;}}tracker;throw"baz";}staticvoidfoo(){structtracker{~tracker(){std::coutthis"destroyed"std::endl;}}tracker;bar();std::cout"dummyoperation"std::endl;}intmain(intargc,char*argv[]){try{foo();}catch(...){std::cout"Icatchit!"std::endl;}//...}

施行以后将会打印:

0x7ffeedestroyed0x7ffeedestroyedIcatchit!

能够直觉得看到栈帧始末了bar→foo→main的经过,由于C++栈上目标释放也是今朝栈帧结尾做的,这边须要额外注意的是foo函数并没有施行竣工,但依然胜利释放了栈上的tracker目标。是以C++在栈帧上溯上的做法必然不是简明地setjmp/longjmp[1]。

0x20RAII整理经过

咱们将代码微小窜改一下:

staticvoidbar(boolshould_throw){structtracker{~tracker(){std::coutthis"destroyed"std::endl;}};trackertracker1;if(should_throw){throw"baz";}trackertracker2;}

这时假如咱们移用bar(false),咱们会看到tracker1和tracker2烧毁的两条日记,但当咱们移用bar(true),由于tracker2构造以前抛出了反常,咱们只会看到tracker1的烧毁日记。这也解说是不是抛出反常,一个栈帧的整理经过是两条不同的routine。

为了搞明青丝生了甚么,咱们能够看下汇编代码:

bar:0x10c18b+0:pushq%rbp0x10c18b+1:movq%rsp,%rbp0x10c18b+4:subq$0x20,%rsp//...0x10c18b+36:testb$0x1,-0x1(%rbp)0x10c18b+40:je0x10c18b98e;跳过抛出反常的逻辑//...0x10c18b+68:callq0x10c25a;symbolstubfor:__cxa_allocate_exception//...0x10c18b96f+95:callq0x10c25a29a;symbolstubfor:__cxa_throw0x10c18b+:jmp0x10c18b9b1;跳转到末了的ud2指令处0x10c18b+:movq%rax,-0x10(%rbp)0x10c18b97d+:movl%edx,-0x14(%rbp)0x10c18b+:leaq-0x8(%rbp),%rdi0x10c18b+:callq0x10c18bae0;bar(bool)::tracker::~tracker()0x10c18b+:jmp0x10c18b9a6;反常路途下tracker析构停止,继承反常处置过程0x10c18b98e+:leaq-0x18(%rbp),%rdi0x10c18b+:callq0x10c18bae0;bar(bool)::tracker::~tracker()0x10c18b+:leaq-0x8(%rbp),%rdi0x10c18b99b+:callq0x10c18bae0;bar(bool)::tracker::~tracker()0x10c18b9a0+:addq$0x20,%rsp0x10c18b9a4+:popq%rbp0x10c18b9a5+:retq0x10c18b9a6+:movq-0x10(%rbp),%rdi0x10c18b9aa+:callq0x10c25a;symbolstubfor:_Unwind_Resume0x10c18b9af+:ud20x10c18b9b1+:ud2;抵达弗成达路途,强迫触发SIGILL停止经过

能够看到咱们的函数中确实存在两条栈帧整理的施行链路。风趣的是,当咱们继承增进新的反常路途,栈帧整理的施行路途也会随之增进。也便是说,当反常产生时,runtime必然要按必然的路途来逐个退出栈帧到exceptionhandler,不能将栈帧直接重置,不然就会激发资本走漏。这个经过叫做StackUnwinding[2],在macOS中,C++ABI应用了libunwind来合营完结反常处置机制。

0x21noexcept关键字

C++中的[noexcept](


转载请注明:http://www.aierlanlan.com/tzrz/844.html

  • 上一篇文章:
  •   
  • 下一篇文章: 没有了