Pull to refresh

Облегчаем поддержку iOS приложения. Часть 1 — не отрываясь от Xcode

Development for iOSDebuggingXcode
Sandbox
Добрый день. Я хотел бы рассказать о том, как можно облегчить поддержку iOS приложений.

  1. Облегчаем поддержку iOS приложения. Часть 1 — не отрываясь от Xcode
  2. Облегчаем поддержку iOS приложения. Часть 2 — локация и сеть
  3. Облегчаем поддержку iOS приложения. Часть 3 — падение и логи


Всем, кто создавал iOS приложение, и оно доходило хотя бы до открытого β-тестирования, скорее всего, знакома фраза: “Я тут поигрался с приложением и вот что получилось...”. И вот после этой фразы вы могли провести несколько часов, пытаясь понять, как же «это» получилось.

Если вам знакома эта ситуация, или хочется узнать о том, как спасти себя от такого в будущем — прошу под кат.


И так, мы создали приложение для себя или для заказчика и идет процесс тестирования. Тестировщики, как и заказчик, могут быть за 1000км от вас. Так что, давайте рассмотрим несколько ситуаций, при которых вам сообщают о странном баге, и вам либо трудно, либо вообще никак не воспроизвести его.

Хит парада — трудно воспроизвести или трудно дебажить.


То есть, либо до проблемы трудно добираться (нужно далеко забраться в приложение с очень особыми условиями, да хоть погода за окном должна быть именно -40, а локация ваша должна быть на экваторе), либо надо много раз повторять одно и то же действие. В последнем случае, то, что вы на каждой итерации попадаете в breakpoint, как-то раздражает, но именно на этой строке кода должно что-то произойти, нам очень надо остановиться именно на ней, когда нам повезет воспроизвести баг.

Все, кто разрабатывает приложения для iOS/MAC, обязательно ставили брейкпоинты и пытались понять, что же происходит перед тем, как приложение падает/ведет себя некорректно. К сожалению, зачастую разработчики используют малую часть функционала, который им доступен. Перерыл Хабр и нашел всего одну статью о полнофунциональном использовании breakpoint’ов. Про отладку приложения с помощью lldb вообще глухо (ну или мои поисковые способности не позволили мне найти нужную статью).

Брейкпоинты
Отличная статья, но я не увидел там описания Symbolic breakpoint. Так что, рекомендую ознакомится со статьей, а я расскажу вам о Symbolic breakpoint'ах.
И так, вы смогли получить тот самый запуск, который воспроизводит некорректное поведение. И, допустим, оно происходит, когда UIViewController только появился, но вот незадача, у вас нету имплементированного метода -[MyViewController viewDidAppear:] и нету ни делегата у UINavigationViewController, ни чего-то похожего в вашем коде, что помогло бы поставить breakpoint в нужный момент. Именно в этой ситуации вам и пригодятся Symbolic breakpoint.

Вам лишь надо вбить символ -[UIViewController viewDidAppear:]

и вы остановитесь в нужный момент.
Еще одно очень полезное применение Symbolic breakpoint — вам достался проект от другого разработчика и есть описание проблемы от тестировщика. И вы понимаете, как попасть туда в приложении, но как сопоставить это с кодом? База кода может быть колоссальной, и анимация намекает, что происходит -[UINavigationController pushViewController:animated:], но вот где это происходит — не ясно. Добавляем Symbolic breakpoint: -[UINavigationController pushViewController:animated:] и мы остановимся на всех вызовах этого метода.
Однако, при использовании Symbolic breakpoint для системных функций, у вас нету ни self, ни _cmd, ничего, что обычно доступно. Так что вам придется подбираться к конроллеру снаружи, и поможет вам lldb.

LLDB
Во время запуcка приложения нажмите на паузу — видите (lldb)? Это консоль дебагера. Есть много полезных команд, которые он умеет выполнять, и самая первая — это help. Теперь можете увидеть остальные полезные команды.
Самая популярная — это po(print object). Она распечатывает -[NSObject debugDescription], в отличие от NSLog, который выводит -[NSObject description]. Стоит иметь это ввиду, когда вы переопределяете description для более удобного логирования в приложении, не забудьте еще и debugDescription.
Продолжим с того места, где остановились: у вас есть консоль и вы в нужном месте приложения. Но, как минимум, надо получить полную иерархию UIView и неплохо бы все ivar'ы MyViewController?
Чтобы узнать указатель на UIWindow нашего приложения, нам надо набрать
po [[[UIApplication sharedApplication] delegate] window]

Мы получили указатель на UIWindow, давайте теперь получим указатель на rootViewController
po [(UIWindow *)<pointer> rootViewController]
Теперь у нас есть указатель на главный контроллер. Аналогичными манипуляциями мы можем дойти до нужного нам контроллера, идя по child'ам или же по property'ям.
И так, у нас есть указатель на наш MyViewController. Допустим, нам нужен его целочисленный _ivar, property для него никто не создавал (незачем нам лишние getter и setter).
Набираем
po ((MyViewController *)<pointer>)->_ivar
Теперь у нас есть все, что нужно.
Вывод информации об объектах — только часть функционала. Вы хотите, чтобы на девайсе анимация стала медленнее, как в симуляторе по нажатию Toggle Slow Animations? Вот вам вариант, как это можно сделать, не перезапуская приложение
expr -- ((CALayer *)[[[[UIApplication sharedApplication] delegate] window] layer]).speed = 0.2

Готово — на время этого запуска у вас медленные анимации и вы можете так делать именно в нужный момент времени и именно в этот запуск. А как надоест, верните значение 1.
Может, эти примеры наивны, но они дают понять базовый функционал. И да, помните, я описал невероятный расклад — -40 && экватор. Теперь вам никто не мешает подменить значения на лету, перед выполнением критичного куска кода, и все будет выглядеть, как будто вы на экваторе и за окном у вас -40.

chisel
Есть один большой минус в предыдущем описании — очень долго и дорого идти до нужного контроллера. Но есть проект, который вам поможет — chisel. Этот товарищ многое умеет, список команд можно найти тут. Если у вас непонятно как лежит вью, потому что она прозрачная, то можно подсветить границы (к примеру, командой mask). Можно, зная текст на кнопке, найти ее (команда pa11y). А если заранее подготовится и сделать так, что текст accessibilityLabel соответствует тексту элемента, то искать можно что угодно. После установки chisel, список его команд добавится в полный список того, что умеет lldb.
Для меня хит парада — это pviews и pvc. Эти команды выведут иерархию UIView и UIViewController. Если вдруг нужно понять, что за команда и как она работает — набираем help <имя команды>.
В общем, рекомендую ознакомиться с этим инструментом, или хотя бы поставить, потому что, когда прижмет и у вас все-таки воспроизведется проблема — остается только рвать волосы на голове, если нету такого помощника под рукой.
Прелестные функции lldb и chisel не заканчиваются вышеперечисленными. В них можно углубляться без конца. Но, на большинстве некрупных проектов, этого будет вполне достаточно.

Attach to Process
Предположим, что хороший задел для дебагинга у нас есть, мы поставили chisel, и теперь можем в нужный момент в lldb набрать help и, благодаря вшитым и доп. командам, понять, что же у нас не так, вот именно тут и вот именно на этом запуске. Все это мы можем сделать, пока наше приложение запущено из Xcode на девайсе или симуляторе. А что же делать, если мы словили баг просто в процессе использования приложения на девайсе? Можно успеть подойти к Xcode и попросить его присоединиться к приложению. Для этого вам нужно Xcode→Debug→Attach to Process. Можно по имени приложения, но у меня Xcode сам угадывает target.

Заключение
Сегодня мы рассмотрели случай, когда у вас есть приложение, идет β-тестирование, есть невероятное описание или же непонятный результат, и никаких идей, как же так получилось. Надеюсь, теперь у вас есть направление, чтобы начать расследование.
Tags:xcodeiosотладка
Hubs: Development for iOS Debugging Xcode
Total votes 5: ↑3 and ↓2+1
Views7.4K

Popular right now

Top of the last 24 hours