在使用Xcode的所有异常断点时忽略特定的异常。

82

我在Xcode中配置了一个所有异常断点:

在Xcode断点窗格中配置的异常断点截图,当抛出异常时播放声音

有时候Xcode会停在这样一行代码上:

[managedObjectContext save:&error];

带有以下回溯信息:

backtrace showing NSPersistentStoreCoordinator throwing an exception inside the call to save:

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

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

(我知道这是因为Core Data内部抛出和捕获异常,并且Xcode只是遵循我的请求,在抛出异常时暂停程序。但是,我想要忽略它们,以便我可以回到调试自己的代码!)

版主:这与“ Xcode 4异常断点过滤”类似,但我认为该问题花费了太长时间才能到达重点,并且没有任何有用的答案。可以将它们链接起来吗?


你能更清楚地解释一下“某些异常”吗? - Jesse Rusak
啊,抱歉!我还没准备好就在 Stack Overflow 上发布了(不小心在标签栏中按下了回车键)。我会编辑的。=( - Phil Calvin
这似乎是与其他问题完全相同的问题。为什么不关闭它并在另一个问题上设置悬赏?如果您认为其他问题不清楚,也可以建议对其进行编辑以使其更加清晰。 - Jesse Rusak
3个回答

95
针对 Core Data 异常,我通常会从 Xcode 中移除 "All Exceptions" 断点,并改用以下方法:
  1. objc_exception_throw 上添加符号断点。
  2. 设置断点条件为 (BOOL)(! (BOOL)[[(NSException *)$x0 className] hasPrefix:@"_NSCoreData"])
配置的断点应该类似于这样: Configuring the Breakpoint 这将忽略任何被用于控制流程的私有 Core Data 异常(由类名以 _NSCoreData 为前缀来确定)。请注意,适当的寄存器将取决于你所运行的目标设备/模拟器。参考此表格
请注意,此技术可以轻松地适应其他条件语句。麻烦之处在于如何通过 BOOL 和 NSException 转换来满足 lldb 对条件的要求。

1
我在设备上尝试使用$r0(BOOL)(! (BOOL)[[(NSException *)$r0 className] hasPrefix:@”_NSCoreData”]),但不起作用。然而,控制台会显示以下内容:由于断点1.1的条件表达式“(BOOL)(! (BOOL)[[(NSException *)$r0 className] hasPrefix:@‚Äù_NSCoreData‚Äù])"无法求值,因此停止执行。 错误:程序中出现意外的'@' 错误:解析表达式时出现1个错误 - lammert
我刚在Xcode 4.6.3 (4H1503)中尝试了这个,但它不喜欢currentName选择器。将其更改为[(NSException *)$eax name]对我有用。 - Adam Sharp
@Blake-Watters 这个解决方法在XCode 5.0.2中似乎不再适用。每次我遇到这些异常时,我都会得到一个无效指令而不是跳过断点。尝试了className和name两种方式。还有更多的想法吗? - LostInTheTrees
4
在iPhone6上,使用iOS 8.2的Xcode 6.2需要我将$r0更改为$x0(如此定义:http://www.sealiesoftware.com/blog/archive/2013/09/12/objc_explain_So_you_crashed_in_objc_msgSend_iPhone_5s_Edition.html)。因此条件变为:```(BOOL)(!(BOOL)[[(NSException *)$x0 className] hasPrefix:@"_NSCoreData"])```。 - Wim Fikkert
1
使用XCode9,我将其修改为"(BOOL)(!(BOOL)[[(NSException *)$arg1 className] hasPrefix:@"_NSCoreData"])"。我喜欢这个修改,因为它也不启用C++异常(AVPlayer在流程中使用了很多内部C++异常...)。 - Gabe
显示剩余5条评论

43

我写了一个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
  • 这将忽略名称匹配NSAccessibilityException或类名匹配NSSomeException的异常

它应该看起来像这样:

Screenshot showing a breakpoint set in Xcode per the instructions

在您的情况下,您将使用ignore_specified_objc_exceptions className:_NSCoreData

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


这对我非常有帮助。您是否愿意直接将脚本和安装说明贡献给Stack Overflow(并以[cc-wiki]许可证(http://creativecommons.org/licenses/by-sa/3.0/)进行许可)?如果是的话,我会接受这个答案! - Phil Calvin
我的目标是将它放到 Stack Overflow 上。如果它只在某个人的网站上,那么它最终会从互联网上消失。我不确定你是否可以合理地向 Stack Overflow 贡献 MIT 许可的内容——它可能最终会成为双重许可,但我不是律师。 - Phil Calvin
您同意您为网络贡献的所有订阅者内容都永久且不可撤销地授权给 Stack Exchange,使用创作共用署名相同许可协议。 - Phil Calvin
1
非常好用。再次强调 - 您需要将断点设置为仅“Objective-C”,因为这里还会抛出C++异常。 - Matej Bukovinski
1
在Xcode 5.1中运行得非常好。一个重要的细节是:您必须选择Objective-C作为异常类型(如说明中所述)。 - Phil Calvin
显示剩余6条评论

18

如果你有一段代码块(例如第三方库)会抛出多个异常,而你想忽略这些异常,那么下面是一种备选的快速方法:

  1. 设置两个断点,一个在要忽略的异常抛出代码块之前,另一个在之后。
  2. 运行程序,直到停在异常处,然后在调试器控制台中输入“breakpoint list”,查找“所有异常”断点的编号,它应该像这样:

2: names = {'objc_exception_throw', '__cxa_throw'}, locations = 2 Options: disabled 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中编辑第一个断点(在异常抛出代码之前),将动作更改为“调试器命令”,并输入“breakpoint disable 2”(并设置“自动继续...”复选框)。

  2. 对于异常发生代码之后的断点也做同样的操作,并使用命令“breakpoint enable 2”。

所有断点异常现在将开启和关闭,因此只有在需要时才会激活它。


谢谢!这正是我想要的。虽然我没有找到异常ID,但我只是用蛮力破解了它。你能详细描述一下那部分吗?我的意思是在Xcode中粘贴“断点列表”以查看断点ID/位置的确切位置在哪里? - vir us
1
很棒 - 这个解决方案比其他答案更直接,而且完美地运作。 - ajgryc
太棒了!这是一个SO答案的宝石,应该被接受。 - j b

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接