Добрый день, сегодня предлагаю вам ознакомиться с переводом статьи об отладке iOS приложений при помощи LLDB.
Одна из самых интригующих частей презентации WWDC 2018, Xcode и продвинутая отладка в LLDB была представлена инженерами Apple. Они дали несколько полезных советов о том, как использовать точки остановки (breakpoints) в Xcode и низкоуровневый дебаггер (LLDB) для оптимизации процесса отладки багов, где бы разработчик их не поймал.
Статья состоит из трех частей, мы пройдемся по основным тезисам, что были сказаны на WWDC. Я создал демо проект специально для того, чтобы тщательнее разобраться как использовать различные типы точек остановки (breakpoints) совместно с LLDB для поимки и отладки багов в вашем приложении.
Демо проект
Я создал проект шаблонных задач, с которыми каждый iOS разработчик так или иначе встречался. Очень важно разобраться с тем как он работает, прежде чем углубляться в чтение статьи. Вот основные функции демо проекта:
- При открытии мы видим table view controller который загружает список постов.
- При прокручивании вниз table view controller подгружает новые посты.
- Всего можно 7 раз подгрузить посты.
- Можно обновить список постов потянув вниз с помощью refresh controller (pull down to refresh).
- В навигационной панели сверху есть 2 лейбла (labels), которые отображают сколько постов было загружено (правый лейбл) и сколько раз пользователь уже подгрузил посты (левый лейбл).
Вы можете скачать демо проект отсюда, если предпочитаете Objective-C.
Если же вам больше по душе Swift, то отсюда.
Запускайте Xcode и поехали!
Баги для исправления!
Итак, вы ознакомились с проектом и наверняка заметили следующие ошибки:
- При протягивании таблицы вниз, рефрешер не обновляет посты.
- Пользователю не приходит никакого оповещения (с помощью alert controller) о том, что HTTP запрос не выполнился из-за проблем с соединением.
- Можно подгрузить посты более 7 раз.
- Левый индикатор (label) в навигационной панели, отвечающий за подсчет количества загрузок, не работает.
Важное правило: до конца этой статьи вы не останавливаете компилятор и не перезапускаете приложение после самого первого запуска. Вы исправляете ошибки во время исполнения программы.
Сила команд-инструкций
Давайте приступим к первому багу.
1. При потягивании таблицы вниз посты не обновляются.
Как воспроизвести ошибку:
- Запустите приложение, первые 10 постов уже загружены.
- Промотайте вниз чтобы загрузить еще посты.
- Промотайте вверх в начало таблицы и потяните её вниз чтобы обновить.
- Новые посты не загружаются и старые не исчезают, а также счетчик постов не сбрасывается.
Стандартный подход для исправления таких ошибок предполагает исследование что происходит внутри селектора метода, отвечающего за UIRefreshControl нашей таблицы. Перейдите в PostsTableViewController в секцию pragma mark Refresh control support. Из функции setupRefreshControl мы можем сделать вывод, что селектор, отвечающий за обновление постов, это функция reloadNewPosts. Давайте добавим брейкпоинт на первую линию этой функции и выясним, что же конкретно здесь происходит. Теперь промотайте вверх в начало таблицы и потяните экран для обновления.
Objective-C
Swift
Отладчик остановился на брейкпоинте, который вы поставили. Для дальнейшего изучения нажмите на кнопку «пропуск блока» отладчика.
Objective-C
Swift
Теперь у нас есть понимание, что же идет не так!
Условие «if» не выполняется (то есть булевая переменная isPullDownToRefreshEnabled выставлена как NO) и, как следствие, код для обновления постов пропускается.
Стандартный подход предполагает остановку компилятора, затем нужно установить isPullDownToRefreshEnabled как YES/true и это решило бы проблему. Но гораздо удобнее было бы сначала проверить нашу гипотезу перед тем как делать какие-то изменения в коде и без необходимости останавливать компилятор. Вот тут-то и оказываются очень полезными команды-инструкции отладчика.
Дважды кликните на установленный брейкпоинт или правой кнопкой мыши выберите «Редактировать брейкпоинт» и нажмите на кнопку «Добавить действие». Так же выберите тип действия (Action) “Debugger Command”.
Теперь нам нужно выставить свойство isPullDownToRefreshEnabled как YES/true. Добавьте следующую команду в отладчик.
Objective-C
expression self.isPullDownToRefreshEnabled = YES
Swift
expression self.isPullDownToRefreshEnabled = true
Далее проверьте чтобы был установлен флажок “Automatically continue after evaluating actions”. Он отвечает за то, чтобы отладчик не останавливался на брейкпоинте каждый раз и автоматически продолжал работу с только что добавленной командой. Теперь промотайте вверх в начало таблицы и потяните вниз для обновления.
Вуаля, новые посты загрузились и заменили старые и, соответственно, счетчик постов обновился.
Мы решили первую проблему, доставайте своё анти-баг оружие, мы приступаем ко второй.
2. Пользователю не приходит никакого оповещения (с помощью alert controller) о том, что HTTP запрос не выполнился из-за проблем с соединением.
Как воспроизвести ошибку:
- Отключите интернет соединение в вашем айфоне/симуляторе.
- Промотайте вверх в начало таблицы и потяните вниз для обновления.
- Новые посты не будут загружены из-за ошибки интернет соединения.
- Пользователю не показывается никакого оповещения об ошибке.
Перейдите в PostsTableViewController в секцию pragma mark Networking. В ней ровно одна функция loadPosts. Она использует общий экземпляр сетевого менеджера для выполнения GET HTTP запроса, который возвращает массив постов через «успешное» замыкание (“success” completion handler) или NSError через «неудачное» замыкание (“failure” completion handler).
Нам надо добавить код в «неудачное» замыкание для того, чтобы вывести alert controller. Если вы перейдете в раздел pragma mark Support, то увидите, что там уже есть функция presentNetworkFailureAlertController, отвечающая за показ нужного alert controller. Все, что нам нужно — вызвать эту функцию внутри «неудачного» замыкания в loadPosts.
Традиционный путь, это остановить симулятор и добавить нужный код. Давайте пойдем другим путем!
Добавьте брейкпоинт внутри «неудачного» замыкания после линии
Objective-C
[self updateUIForNetworkCallEnd];
Swift
self.updateUIForNetworkCallEnd()
Дважды кликните на установленный брейкпоинт или правой кнопкой мыши выберите «Редактировать брейкпоинт» и нажмите на кнопку «Добавить действие». Так же выберите тип действия (Action) “Debugger Command”.
Добавьте следующую команду в отладчик.
Objective-C
expression [self presentNetworkFailureAlertController]
Swift
expression self.presentNetworkFailureAlertController()
Проверьте чтобы был установлен флажок “Automatically continue after evaluating actions”.
С отключенным интернетом, долистайте вверх до начала таблицы и потяните вниз для обновления, или же вы можете пролистать вниз до конца таблицы и попробовать подгрузить новые посты. Вот что вы увидите:
Что мы только что сделали, называется «внедрение» кода с помощью команды добавленной в отладчик в конкретно заданный брейкпоинт.
Небольшой итог
Давайте еще раз перечислим, что же мы можем сделать с помощью команд отладчика, добавленных в брейкпоинт:
- Оперировать существующим значением свойства.
- Добавлять новую строку кода.
Оба задания были сделаны во время выполнения программы. Нам по сути не нужно останавливать компилятор для исправления ошибок и перезапускать приложение.
Что же дальше?
Дальше предлагаю вашему вниманию вторую часть статьи, где мы исправим еще ошибки и узнаем об еще одном типе точки остановки (breakpoint) – точка наблюдения (watchpoint).
Автор: kosyakus