80

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

Xcode断点痛中配置的异常断点截图,配置为抛出异常时发出声音

有时 Xcode 会停在如下一行:

[managedObjectContext save:&error];

具有以下回溯:

回溯显示 NSPersistentStoreCoordinator 在调用保存时抛出异常:

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

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

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

版主:这类似于“Xcode 4 异常断点过滤”,但我认为这个问题需要很长时间才能解决问题并且没有任何有用的答案。他们可以联系起来吗?

4

3 回答 3

89

对于核心数据异常,我通常做的是从 Xcode 中删除“所有异常”断点,而是:

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

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

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

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

于 2013-01-17T03:17:28.323 回答
42

我编写了一个 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

它应该看起来像这样:

屏幕截图显示了按照说明在 Xcode 中设置的断点

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

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

于 2013-10-09T02:52:47.813 回答
17

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

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

2:名称= {'objc_exception_throw','__cxa_throw'},位置= 2选项:禁用2.1:其中= libobjc.A.dylib objc_exception_throw, address = 0x00007fff8f8da6b3, unresolved, hit count = 0 2.2: where = libc++abi.dylib__cxa_throw,地址= 0x00007fff8d19fab7,未解决,命中计数= 0

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

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

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

于 2014-12-05T13:35:52.880 回答