如何阻止HIToolbox捕获我的异常?

时间:2010-09-06 14:59:06

标签: objective-c cocoa exception-handling macos-carbon

关于why my app isn't being brought down by exceptions的其他问题,我们提出了这个问题。

问题

当通过Action在主线程上抛出异常时,应用程序仍然不会崩溃。

根据Dave's answer to my original question,我在NSApplication上实现了reportException类,并设置了未捕获的异常处理程序。

代码

我在我的app委托中有以下内容,我已经连接到我的UI中的按钮进行测试。

-(IBAction)crashOnMainThread:(id)sender {
    [self performSelectorOnMainThread:@selector(crash) withObject:nil waitUntilDone:YES];
}

-(void)crash {
    // To test out the exception handling
    [NSException raise:NSInternalInconsistencyException format:@"This should crash the app."];
}

当我按下按钮时,我的应用程序不会崩溃。当我查看控制台日志时,我看到了:

06/09/2010 14:12:25 EHTest1[26384]  HIToolbox: ignoring exception 'This should crash the app.' that raised inside Carbon event dispatch
(
    0   CoreFoundation                      0x00007fff80ab4cc4 __exceptionPreprocess + 180
    1   libobjc.A.dylib                     0x00007fff819560f3 objc_exception_throw + 45
    2   CoreFoundation                      0x00007fff80ab4ae7 +[NSException raise:format:arguments:] + 103
    3   CoreFoundation                      0x00007fff80ab4a74 +[NSException raise:format:] + 148
    4   EHTest1                             0x00000001000010e3 -[EHTest1_AppDelegate crashLapsus] + 63
    5   Foundation                          0x00007fff88957c25 -[NSObject(NSThreadPerformAdditions) performSelector:onThread:withObject:waitUntilDone:modes:] + 234
    6   Foundation                          0x00007fff8896ad48 -[NSObject(NSThreadPerformAdditions) performSelectorOnMainThread:withObject:waitUntilDone:] + 143
    7   EHTest1                             0x0000000100001030 -[EHTest1_AppDelegate crashOnMainThread:] + 60
    8   AppKit                              0x00007fff85c7e152 -[NSApplication sendAction:to:from:] + 95
    9   AppKit                              0x00007fff85ca26be -[NSMenuItem _corePerformAction] + 365

    ** Snip **

看起来Carbon正在捕捉异常,这真的很烦人。

这表明对于任何操作代码,您需要立即在后台线程中运行它,以便将任何异常注册为未捕获。咦?我没见过任何像这样的结构代码。

我尝试了什么

延迟崩溃应用程序,因此它未连接到UI元素。它崩溃了。

我已尝试在我的app delegate中使用此安装自定义NSExceptionHandler:

-(BOOL)exceptionHandler:(NSExceptionHandler *)sender 
  shouldHandleException:(NSException *)exception 
                   mask:(unsigned int)aMask {
      abort();
      return YES;
}

-(void)applicationWillFinishLaunching:(NSNotification *)aNotification {
      NSExceptionHandler *handler = [NSExceptionHandler defaultExceptionHandler];
      [handler setExceptionHandlingMask:NSLogAndHandleEveryExceptionMask];
      [handler setDelegate:self];
}

这里的问题是它会在每个异常上崩溃,无论它是否被捕获。

如果我尝试检查掩码并且没有在捕获的异常上崩溃,我会回到正方形1,因为看起来HIToolbox以与try / catch块完全相同的方式捕获异常。

问题

  • 如何阻止HIToolbox捕获异常,以便我的应用程序使用未捕获的异常处理程序并崩溃?
  • 运行与动作在同一个调用堆栈中的代码是否可以?当然可以吗?
  • 如果不行,还有什么选择?

这让我起了作用,所以任何帮助都会非常感激。

2 个答案:

答案 0 :(得分:6)

我回答了关于这个主题的最后一个问题,并且遇到了与Carbon的HIToolbox相同的问题,捕获了IBActions抛出的异常。

首先,撤消我在previous answer中提到的所有内容。由于某种原因,它不适用于IBActions。我的预感是HIToolbox在异常处理链上的寿命较低,并且在NSApplication有机会之前获得任何IBAction / GUI异常。任何自定义异常处理函数,您都可以注册NSSetUncaughtExceptionHandler()生命(我相信)在链的顶端。

你在NSExceptionHandling的正确轨道上:

  1. ExceptionHandling.framework 添加到您的Xcode项目
  2. #import "ExceptionHandlerDelegate.h"到您的AppDelegate.m(或自定义Singleton异常类)
  3. 内部 AppDelegate.m

    // Very first message sent to class
    + (void)initialize
    {
        NSExceptionHandler *exceptionHandler = [NSExceptionHandler defaultExceptionHandler];
        unsigned int handlingMask = NSLogAndHandleEveryExceptionMask;
        [exceptionHandler setExceptionHandlingMask:handlingMask];
        [exceptionHandler setDelegate:self];
    
        // ...
    }
    
    
    #pragma mark -
    #pragma mark NSExceptionHandler Delegate Methods
    
    // Called 1st: Should NSExceptionHandler log the exception?
    - (BOOL)exceptionHandler:(NSExceptionHandler *)sender shouldLogException:(NSException *)exception mask:(unsigned int)aMask
    {
        // ...log NSException if desired...
    
        return NO;  // Changes to YES if NSExceptionHandler should handle logging
    }
    
    // Called 2nd: Should NSExceptionHandler handle the exception?
    - (BOOL)exceptionHandler:(NSExceptionHandler *)sender shouldHandleException:(NSException *)exception mask:(unsigned int)aMask
    {
        // ...crash your app here (e.g. [NSApp terminate:self]; )
    
        // ...or handle the NSException and return YES/NO accordingly
    
        return NO;  // If app crashed, never gets returned - should crash before that
    }
    

    NSLogAndHandleEveryExceptionMask标志告诉NSExceptionHandler捕获它可能的每个异常(我认为只适用于你的应用程序),无论它存在于异常链的哪个位置。

    这意味着 @ catch / @ try / @ finally 块将无效,因为NSHandleOtherExceptionMask标志告诉NSExceptionHandler在异常处理程序链上捕获“它下面的所有内容” 。您可以删除该标志,但是HIToolKit可能会再次获得任何IBAction异常,因为它在所述链上看起来较低。

    Apple的文档有关于旗帜的信息:NSExceptionHandler docs

    因此,当引发NSException时(应用程序AFAIK中的任何位置),并且设置了NSLogAndHandleEveryExceptionMask,这些将在委托中按顺序调用:

      首先调用
    1. -exceptionHandler:shouldLogException:mask:
    2. -exceptionHandler:shouldHandleException:mask:被称为第二名。
    3. 只需将“崩溃代码”放在第二个委托方法中,您就可以了。


      有用的文章:Understanding Exceptions and Handlers in Cocoa


      我认为您无法使NSExceptionHandler的委托工作的原因是因为它与使用NSSetUncaughtExceptionHandler()的自定义方法集不兼容,这是我上一个问题的答案的一部分。每Apple

        

      NSExceptionHandler类提供   监测设施   调试中的异常条件   Objective-C计划。它的工作原理   安装一个特殊的未被捕获   异常处理程序通过   NSSetUncaughtExceptionHandler   功能。 因此,要使用   NSExceptionHandler的服务,你   不得安装自己的自定义   未捕获的异常处理程序。

      当您覆盖NSApplication的-reportException:方法时,它也可能无法正常工作。

      最后,似乎没有必要使用@ catch / @ try / @ finally(也是我以前的答案的一部分)。与覆盖NSApplication的+initialize方法不同,在-reportException:内部配置NSExceptionHandler似乎会立即“启动”。

答案 1 :(得分:1)

你不能可靠。除非明确记录,否则不支持跨API边界抛出异常(我无法想到明确记录的任何情况)。