如何阻止 HIToolbox 捕获异常?

发布于 2024-09-17 17:50:43 字数 3180 浏览 7 评论 0原文

这个问题是我关于为什么我的应用程序的另一个问题的延续没有被异常所影响

问题

当通过操作在主线程上引发异常时,应用程序仍然不会崩溃。

根据 戴夫对我原来问题的回答 ,我在 NSApplication 上实现了 reportException 类别并设置了未捕获的异常处理程序。

代码

我的应用程序委托中有以下内容,我已将其连接到 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 元素。它崩溃得很好。

我尝试在我的应用程序委托中使用它来安装自定义 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 捕获异常,以便我的应用程序使用未捕获的异常处理程序并崩溃?
  • 运行与操作位于同一调用堆栈中的代码是否可以?这当然可以吗?
  • 如果不行,还有什么替代方案?

这让我陷入困境,所以任何帮助将不胜感激。

This question follows on from my other question on why my app isn't being brought down by exceptions.

The Problem

When an exception is thrown on the main thread via an Action, the app still doesn't crash.

As per Dave's answer to my original question, I've implemented the reportException category on NSApplication and set the uncaught exception handler.

Code

I've got the following in my app delegate, which I've hooked up to a button in my UI to test.

-(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."];
}

When I press the button, my app doesn't crash. When I look at the console log, I see this:

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 **

It looks like Carbon is catching the exception, which is really annoying.

This suggests that for any action code, you need to immediately run it in a background thread so any exceptions are registered as uncaught. Huh? I've not seen any code that's structured like this.

What I've tried

Crashing the app with a delay so it's not connected to a UI element. It crashes fine.

I've tried installing a custom NSExceptionHandler using this in my app delegate:

-(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];
}

the problem here is it crashes on every exception, whether it's caught or not.

If I try and check the mask and don't crash on a caught exception, I'm back to square 1 as it seems that HIToolbox catches the exception in exactly the same way as a try/catch block would.

Questions

  • How can I stop HIToolbox catching the exceptions so that my app uses the uncaught exception handler and crashes?
  • Is it OK to be running code that's in the same call stack as an action? Surely this is OK?
  • If it's not OK, what's the alternative?

This is driving me up the wall, so any help would be much appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

雨落星ぅ辰 2024-09-24 17:50:43

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

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

NSExceptionHandling 的方向是正确的:

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

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(据我所知在应用程序中的任何位置)并设置 NSLogAndHandleEveryExceptionMask 时,这些将在委托中按顺序调用:

  1. -首先调用ExceptionHandler:shouldLogException:mask:
  2. -exceptionHandler:shouldHandleException:mask: 第二个被调用。

只需将您的“崩溃代码”放入第二个委托方法中就可以了。


有用的文章:了解 Cocoa 中的异常和处理程序


我认为您在让 NSExceptionHandler 的委托工作时遇到问题的原因是它与使用 设置的自定义方法不兼容NSSetUncaughtExceptionHandler(),这是我上一个问题的答案的一部分。每 苹果

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

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

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

I answered your last question on this subject, and ran into the same problem with Carbon's HIToolbox catching exceptions thrown by IBActions.

First, undo everything I mentioned in my previous answer. It doesn't work with IBActions for some reason. My hunch is that HIToolbox lives lower on the exception-handling-chain, and gets any IBAction/GUI exceptions before NSApplication has the opportunity to. Any custom exception-handling function you can register with NSSetUncaughtExceptionHandler() lives (I believe) at the top of the chain.

You're on the right track with NSExceptionHandling:

  1. Add the ExceptionHandling.framework to your Xcode Project
  2. #import "ExceptionHandlerDelegate.h" into your AppDelegate.m (or a custom Singleton exception class)

Inside 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
}

The NSLogAndHandleEveryExceptionMask flag tells NSExceptionHandler to capture every exception it can (for your app only, I believe), regardless of where on the exception chain it exists.

This means that @catch/@try/@finally blocks won't work, because the NSHandleOtherExceptionMask flag tells NSExceptionHandler to capture "everything below it" on the exception-handler chain. You can remove that flag, but then HIToolKit will probably get any IBAction exceptions again, since it appears to be lower on said chain.

Apple's docs have info about the flags: NSExceptionHandler docs

So when an NSException is raised (anywhere in your app AFAIK), and NSLogAndHandleEveryExceptionMask is set, these are called in the delegate in-order:

  1. -exceptionHandler:shouldLogException:mask: is called first.
  2. -exceptionHandler:shouldHandleException:mask: is called second.

Just put your "crash code" inside the second delegate method and you should be OK.


Helpful article: Understanding Exceptions and Handlers in Cocoa


The reason I think you were having trouble getting NSExceptionHandler's delegate to work is because it's NOT compatible with a custom method set with NSSetUncaughtExceptionHandler(), which was part of the answer in my previous question. Per Apple:

The NSExceptionHandler class provides
facilities for monitoring and
debugging exceptional conditions in
Objective-C programs. It works by
installing a special uncaught
exception handler via the
NSSetUncaughtExceptionHandler
function. Consequently, to use the
services of NSExceptionHandler, you
must not install your own custom
uncaught exception handler.

It also probably doesn't work well when you override NSApplication's -reportException: method.

Lastly, there doesn't appear to be a need to use @catch/@try/@finally (also part of my previous answer). Configuring NSExceptionHandler inside +initialize appears to "kick in" immediately, unlike overriding NSApplication's -reportException: method.

尘世孤行 2024-09-24 17:50:43

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

You can’t reliably. Throwing exceptions across API boundaries is not supported unless explicitly documented (and I can’t think of any cases that are explicitly documented).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文