Игнорировать некоторые исключения, когда, используя все исключения останова в Xcode


У меня есть все исключения точка останова настроена в Xcode:

иногда Xcode останавливается на строке типа:

[managedObjectContext save:&error];

со следующим обратным следом:

но программа продолжает работать, как будто ничего не произошло, если вы нажмете кнопку Продолжить.

как я могу игнорировать эти "обычные" исключения, но все же остановить отладчик на исключениях в моем собственном коде?

(Я понимаю, что это происходит потому, что Core Data внутренне бросает и ловит исключения, и что Xcode просто выполняет мой запрос на приостановку программы всякий раз, когда возникает исключение. Однако я хочу игнорировать их, чтобы я мог вернуться к отладке своего собственного кода!)

модераторы: это похоже на "фильтрация точек останова исключения Xcode 4", но я думаю, что этот вопрос занимает слишком много времени, чтобы добраться до сути и не имеет никаких полезных ответов. Могут ли они быть связаны?

3 71

3 ответа:

я написал сценарий lldb, который позволяет выборочно игнорировать исключения Objective-C с гораздо более простым синтаксисом, и он обрабатывает как OS X, iOS Simulator, так и 32bit и 64bit 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 *) {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 добавьте точку останова, чтобы поймать все С исключения
  • измените точку останова и добавьте команду отладчика с помощью следующей команды: ignore_specified_objc_exceptions name:NSAccessibilityException className:NSSomeException
  • это будет игнорировать исключения, где NSException-name игр NSAccessibilityException или -className игр 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/ для сценария и более подробной информации.

для исключений основных данных я обычно удаляю точку останова "все исключения" из Xcode и вместо этого:

  1. добавить символическую точку останова на objc_exception_throw
  2. установите условие для точки останова в (BOOL)(! (BOOL)[[(NSException *)$x0 className] hasPrefix:@"_NSCoreData"])

настроенная точка останова должна выглядеть примерно так: Configuring the Breakpoint

это будет игнорировать любые частные исключения данных ядра (как определено именем класса с префиксом _NSCoreData), которые используются для контроля поток. Обратите внимание, что соответствующий регистр будет зависеть от целевого устройства / симулятора, в котором вы работаете. Взгляните на в этой таблице для справки.

обратите внимание, что этот метод может быть легко адаптирован для других условий. Сложная часть заключалась в создании bool и nsexception casts, чтобы получить lldb счастливым с условием.

вот альтернативный быстрый ответ, когда у вас есть блок кода, например, библиотека 3-й части, которая выдает несколько исключений, которые вы хотите игнорировать:

  1. установите две точки останова, одну до и одну после исключения бросая блок кода, который вы хотите игнорировать.
  2. запустите программу, пока она не остановится на исключении, и введите "список точек останова" в консоль отладчика и найдите номер точки останова "все исключения", он должен выглядеть так это:

2: names = {'objc_exception_throw', '__cxa_throw'}, locations = 2 Опции: отключено 2.1: где = libobjc.Dylib нужна А.objc_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. сделайте то же самое для точки останова после оскорбительной строки и выполните команду "точка останова включить 2".

исключение все точки останова теперь будет включаться и выключаться, поэтому он активен только тогда, когда вам это нужно.