Ігноруйте певні винятки під час використання точки зупинки "Усі винятки" Xcode


78

У мене є точка виключення All Exceptions, налаштована в Xcode:

знімок екрана точки зупинки винятку, налаштованої в Xcode біль точки зупинки, налаштованої видавати звук при виникненні винятку

Іноді Xcode зупиняється на такому рядку, як:

[managedObjectContext save:&error];

з наступним зворотним відстеженням:

зворотне відстеження, що показує NSPersistentStoreCoordinator, що викидає виняток всередині виклику для збереження:

але програма продовжує працювати так, ніби нічого не сталося, якщо натиснути кнопку Продовжити.

Як я можу проігнорувати ці "звичайні" винятки, але все-таки налагоджувач зупиняється на винятках у власному коді?

(Я розумію, що це відбувається тому, що Core Data внутрішньо викидає та ловить винятки, і що Xcode просто виконує моє прохання призупинити програму, коли виникає виняток. Однак я хочу ігнорувати їх, щоб я міг повернутися до налагодження власного коду !)

Модератори: це схоже на "фільтрацію точок зупинки винятків Xcode 4" , але я думаю, що це питання займає занадто багато часу, щоб дійти до суті, і не має корисних відповідей. Чи можна їх пов’язати?


Чи можете ви бути більш чіткими щодо "певних винятків"?
Джессі Русак

Арх, вибачте! Переповнення стеку опубліковано до моєї готовності (випадково вдарив Enter у полі тегів.) Я відредагую. = (
Phil Calvin

Це здається точно таким же питанням, як і інше. Як щодо того, щоб закрити його і покласти щедрість на іншого? Ви також можете запропонувати редагування іншому, щоб очистити його, якщо ви вважаєте це незрозумілим.
Джессі Русак,

Відповіді:


41

Я написав скрипт lldb, який дозволяє вибірково ігнорувати винятки Objective-C з набагато простішим синтаксисом, і він обробляє як OS X, iOS Simulator, так і 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/ для отримання додаткових відомостей.


Це дуже добре працює для мене. Чи хотіли б ви внести свій сценарій та інструкції з встановлення безпосередньо в Stack Overflow (і, отже, ліцензувати їх як cc-wiki ?) Якщо так, я прийму цю відповідь!
Філ Кальвін,

Я повинен був позначити @chendo в цій останній відповіді.
Філ Кальвін,

@PhilCalvin Чи буде ліцензування як MIT краще?
chendo

1
Чудово працює. Щоб ще раз підкреслити - вам потрібно встановити точку зупинки просто "Objective-C", оскільки тут також є виняток C ++.
Matej Bukovinski

1
Працює дуже добре в Xcode 5.1. Важлива деталь: вам потрібно вибрати Objective-C як тип виключення (як зазначено в інструкції.)
Філ Кальвін,

87

Для винятків основних даних я зазвичай видаляю точку зупинки "Усі винятки" із Xcode і замість цього:

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

Налаштована точка зупинки повинна виглядати приблизно так: Налаштування точки зупинку

Це буде ігнорувати будь-які приватні винятки Основних даних (як визначається префіксом імені класу _NSCoreData), які використовуються для керування потоком. Зверніть увагу, що відповідний регістр буде залежати від цільового пристрою / симулятора, в якому ви працюєте. Погляньте на цю таблицю для довідки.

Зверніть увагу, що цю техніку можна легко адаптувати до інших умов. Хитра частина полягала в розробці зліпків BOOL та NSException, щоб зробити lldb задоволеним станом.


Дуже корисно, особливо при використанні основних даних у декількох потоках, і ці помилкові винятки викидаються набагато частіше! Дуже дякую!
bmasters

1
Я намагаюся це на пристрої з $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

@lammert, можливо, ви захочете замінити цитати, скопійовані з прикладу, справжніми цитатами. вміст, який ви скопіювали, включає розроблені цитати.
Jeremy Foo

Я щойно спробував це в Xcode 4.6.3 (4H1503), і currentNameселектор йому не сподобався . Зміна цього на [(NSException *)$eax name]мене спрацювало.
Адам Шарп

4
Xcode 6.2 з iOS 8.2 на iPhone6 ​​вимагав від мене змінити значення $r0на $x0(як визначено тут: sealiesoftware.com/blog/archive/2013/09/12/… ). Таким чином, умова стає:(BOOL)(! (BOOL)[[(NSException *)$x0 className] hasPrefix:@"_NSCoreData"])
Вім Фіккерт

16

Ось альтернативна швидка відповідь, коли у вас є блок коду, наприклад, бібліотека третьої частини, яка видає кілька винятків, які ви хочете ігнорувати:

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

2: names = {'objc_exception_throw', '__cxa_throw'}, locations = 2 Параметри: вимкнено 2.1: where = libobjc.A.dylib objc_exception_throw, address = 0x00007fff8f8da6b3, unresolved, hit count = 0 2.2: where = libc++abi.dylib__cxa_throw, address = 0x00007fff8d19fab7, невирішено, count hit = 0

  1. Це означає, що це точка зупинки 2. Тепер у xcode відредагуйте першу точку зупинки (перед кодом, що видає виняток) і змініть дію на 'команду налагоджувача' та введіть 'пункт зупинки вимкнути 2' (і встановіть прапорець 'автоматично продовжити ...' ).

  2. Зробіть те ж саме для точки розриву після порушувального рядка і отримайте команду 'точка зупинки увімкнути 2'.

Тепер виключення з усіх точок зупинки вмикається та вимикається, тому воно активне лише тоді, коли це потрібно.


Дякую! саме те, що я шукав. Хоча мені не вдалося знайти ідентифікатор винятку - просто грубий вимушений. Чи можете ви описати цю частину більш докладно? Я маю на увазі, куди саме в Xcode вставити цей "список точок зупинки", щоб побачити ідентифікатор точки зупинки / розташування?
vir us

Дивовижний! Працює як шарм!
Сергій Грищов

1
Відмінно - це рішення набагато простіше, ніж деякі інші відповіді, і працює ідеально.
ajgryc

Блискуче! Це перлина SO-відповіді, і вона повинна бути прийнятою.
jb
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.