【问题标题】:Ignore certain exceptions when using Xcode's All Exceptions breakpoint使用 Xcode 的 All Exceptions 断点时忽略某些异常
【发布时间】:2012-12-31 11:50:31
【问题描述】:

我在 Xcode 中配置了一个 All Exceptions 断点:

有时 Xcode 会停在如下一行:

[managedObjectContext save:&error];

使用以下回溯:

但如果您单击“继续”,程序会继续运行,就好像什么都没发生一样。

如何忽略这些“正常”异常,但仍让调试器停止我自己代码中的异常?

(我知道发生这种情况是因为 Core Data 在内部抛出并捕获异常,并且 Xcode 只是尊重我在抛出异常时暂停程序的请求。但是,我想忽略这些,以便我可以重新开始调试我自己的代码!)

版主:这类似于"Xcode 4 exception breakpoint filtering",但我认为这个问题需要很长时间才能解决问题并且没有任何有用的答案。可以关联吗?

【问题讨论】:

  • “某些例外”你能再清楚一点吗?
  • 啊,对不起! Stack Overflow 在我准备好之前发布(不小心在标签字段中按了 Enter。)我将进行编辑。 =(
  • 这似乎与另一个问题完全相同。关闭它并在另一个上放一个bounty 怎么样?如果您认为不清楚,也可以建议对另一个进行编辑以进行清理。

标签: iphone ios xcode


【解决方案1】:

对于 Core Data 异常,我通常会从 Xcode 中删除“所有异常”断点,而是:

  1. objc_exception_throw 上添加符号断点
  2. 将断点上的条件设置为(BOOL)(! (BOOL)[[(NSException *)$x0 className] hasPrefix:@"_NSCoreData"])

配置的断点应该如下所示:

这将忽略用于控制流的任何私有核心数据异常(由以_NSCoreData 为前缀的类名确定)。请注意,适当的寄存器将取决于您正在运行的目标设备/模拟器。请查看this table 以供参考。

请注意,这种技巧可以很容易地适应其他条件句。棘手的部分是制作 BOOL 和 NSException 强制转换以使 lldb 对条件感到满意。

【讨论】:

  • 非常有帮助,尤其是在多线程上使用 Core Data 并且这些错误异常更频繁地抛出时!非常感谢!
  • 我正在使用$r0:(BOOL)(! (BOOL)[[(NSException *)$r0 className] hasPrefix:@”_NSCoreData”]) 在设备上尝试这个。但这不起作用。我在控制台中得到以下信息。 Stopped due to an error evaluating condition of breakpoint 1.1: "(BOOL)(! (BOOL)[[(NSException *)$r0 className] hasPrefix:@‚Äù_NSCoreData‚Äù])" error: unexpected '@' in program error: 1 errors parsing expression
  • @lammert 您可能希望将您从示例中复制的引号替换为实际引号。您复制的内容包括美化引号。
  • Xcode 6.2 和 iPhone6 上的 iOS 8.2 要求我将 $r0 更改为 $x0(定义如下:sealiesoftware.com/blog/archive/2013/09/12/…)。因此条件变为:(BOOL)(! (BOOL)[[(NSException *)$x0 className] hasPrefix:@"_NSCoreData"])
  • 使用 XCode9 我将其修改为“(BOOL)(!(BOOL)[[(NSException *)$arg1 className] hasPrefix:@"_NSCoreData"])”。我喜欢这个,因为它也没有启用 C++ 异常(AVPlayer 使用大量内部 C++ 异常来处理流程......)
【解决方案2】:

我编写了一个 lldb 脚本,它允许您使用更简单的语法选择性地忽略 Objective-C 异常,它可以处理 OS X、iOS 模拟器以及 32 位和 64 位 ARM。

安装

  1. 将此脚本放在~/Library/lldb/ignore_specified_objc_exceptions.py 或其他有用的地方。
import lldb
import re
import shlex

# This script allows Xcode to selectively ignore Obj-C exceptions
# based on any selector on the NSException instance

def getRegister(target):
    if target.triple.startswith('x86_64'):
        return "rdi"
    elif target.triple.startswith('i386'):
        return "eax"
    elif target.triple.startswith('arm64'):
        return "x0"
    else:
        return "r0"

def callMethodOnException(frame, register, method):
    return frame.EvaluateExpression("(NSString *)[(NSException *)${0} {1}]".format(register, method)).GetObjectDescription()

def filterException(debugger, user_input, result, unused):
    target = debugger.GetSelectedTarget()
    frame = target.GetProcess().GetSelectedThread().GetFrameAtIndex(0)

    if frame.symbol.name != 'objc_exception_throw':
        # We can't handle anything except objc_exception_throw
        return None

    filters = shlex.split(user_input)

    register = getRegister(target)


    for filter in filters:
        method, regexp_str = filter.split(":", 1)
        value = callMethodOnException(frame, register, method)

        if value is None:
            output = "Unable to grab exception from register {0} with method {1}; skipping...".format(register, method)
            result.PutCString(output)
            result.flush()
            continue

        regexp = re.compile(regexp_str)

        if regexp.match(value):
            output = "Skipping exception because exception's {0} ({1}) matches {2}".format(method, value, regexp_str)
            result.PutCString(output)
            result.flush()

            # If we tell the debugger to continue before this script finishes,
            # Xcode gets into a weird state where it won't refuse to quit LLDB,
            # so we set async so the script terminates and hands control back to Xcode
            debugger.SetAsync(True)
            debugger.HandleCommand("continue")
            return None

    return None

def __lldb_init_module(debugger, unused):
    debugger.HandleCommand('command script add --function ignore_specified_objc_exceptions.filterException ignore_specified_objc_exceptions')
  1. 将以下内容添加到~/.lldbinit

    command script import ~/Library/lldb/ignore_specified_objc_exceptions.py

    如果您将 ~/Library/lldb/ignore_specified_objc_exceptions.py 保存在其他位置,请将其替换为正确的路径。

用法

  • 在 Xcode 中,添加断点以捕获所有 Objective-C 异常
  • 编辑断点并使用以下命令添加调试器命令: ignore_specified_objc_exceptions name:NSAccessibilityException className:NSSomeException
  • 这将忽略 NSException -name 匹配 NSAccessibilityException-className 匹配 NSSomeException 的异常

它应该看起来像这样:

在你的情况下,你会使用ignore_specified_objc_exceptions className:_NSCoreData

有关脚本和更多详细信息,请参阅http://chen.do/blog/2013/09/30/selectively-ignoring-objective-c-exceptions-in-xcode/

【讨论】:

  • 这对我来说非常有效。您是否愿意将您的脚本和安装说明直接贡献给 Stack Overflow(并因此将它们许可为 cc-wiki?)如果是这样,我会接受这个答案!
  • 我应该在最后的回复中标记@chendo。
  • @PhilCalvin 将其作为 MIT 许可会更好吗?
  • 效果很好。再次强调一下 - 您需要将断点设置为“Objective-C”,因为这里还抛出了一个 C++ 异常。
  • 在 Xcode 5.1 中工作得非常好。一个重要的细节:您必须选择 Objective-C 作为异常类型(如说明中所述)。
【解决方案3】:

当您有代码块时,这是一个替代的快速答案,例如第 3 部分库,它会引发您想要忽略的多个异常:

  1. 设置两个断点,一个在要忽略的异常抛出代码块之前,一个在之后。
  2. 运行程序,直到在异常处停止,然后在调试器控制台中输入“断点列表”,找到“所有异常”断点的编号,应该如下所示:

2:名称 = {'objc_exception_throw','__cxa_throw'},位置 = 2 选项:禁用 2.1: where = libobjc.A.dylibobjc_exception_throw, address = 0x00007fff8f8da6b3, unresolved, hit count = 0 2.2: where = libc++abi.dylib__cxa_throw, address = 0x00007fff8d19fab7, unresolved, hit count = 0

  1. 这意味着它是断点 2。现在在 xcode 中,编辑第一个断点(在异常抛出代码之前)并将操作更改为“调试器命令”并输入“断点禁用 2”(并设置“自动继续...' 复选框)。

  2. 对违规行之后的断点执行相同操作,并使用命令“breakpoint enable 2”。

所有断点异常现在将打开和关闭,因此它仅在您需要时才处于活动状态。

【讨论】:

  • 谢谢!正是我想要的。虽然我无法找到异常 id - 只是蛮力强迫它。你能更详细地描述那部分吗?我的意思是在 Xcode 中粘贴“断点列表”以查看断点 ID/位置的确切位置?
  • 优秀 - 这个解决方案比其他一些答案更简单,而且效果很好。
  • 太棒了!这是一个很好的答案,应该是被接受的。
猜你喜欢
  • 2017-02-22
  • 2012-10-03
  • 2016-10-18
  • 1970-01-01
  • 2012-11-10
  • 1970-01-01
  • 2012-06-07
  • 2015-07-06
  • 1970-01-01
相关资源
最近更新 更多