iOs Debug Master

от автора

Однажды я почти полностью отказался от мышки для навигации по Xcode и вполне этому рад. Следующий шаг — это отказ от визуальных средств управления отладчиком. Зачем? — Увеличиваем возможности, уменьшаем время дебага, тратим меньше калорий для перемещения тяжеленькой ручишки (нам калории нужны, чтобы головой работать) и тем самым провоцируем меньше туннельного синдрома.


.
Особо новых открытий в статье не будет, просто подытожу, что вы и сами хорошо знали и как это можно использовать для кого-то по-новому.

Не рассматриваю такие темы: как работать в Xcode, как вообще отлаживать приложение, в какой момент нужно это делать и зачем, что такое LLDB, что такое Step Into и Step Over и т.д.

Сразу к примерам:

Настраиваем консоль

Отказываем вот от этой панельки:

Да и вот про эту скоро забудем

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

Спасибо моему коллеге: он осуществил мою давнишнюю мечту — открывать консоль отладчика в новом окне. А то всегда напрягало, что только открыл и настроил вкладки как тебе надо, так сразу же при первом брейкпоинте всё запоролось. Чтобы этого не было идем Xcode -> Behaviours -> Edit behaviours… Далее нам нужна секция Running -> Pause.
Выбираем Show tab named, пишем туда наше уникальное название вкладки, например Debug (замечу, что при повторных запусках вкладка не дублируется), в конце ставим active window — это, как ни странно, открывает новую вкладку (Cmd + T) для нашего отладчика. Ешё я скрыл ненужную правую панель — но это по желанию. Вообще кастомизация среды при разных условиях — это в разделе Behaviours

image

Всё, мы настроены, двигаем дальше.

Управляем отладчиком

Добавляем сразу в копилку полезных hotkey’ев кто не знал Cmd + Shift + C. Это быстрый переход в консоль.
Сmd + Shift + Y — это скрыть/показать консоль.
Наши новые команды:
Step Intostep или коротко s
Step Overnext или коротко n
Step Outfinish
Continuecontinue или c
Отключить все брейкпоинты — breakpoint disable
За полным списком команд lldb можно отправиться вот сюда
Не буду перечислять все возможности прямых команд LLDB, но их список больше, чем позволяет сделать визуальная среда Xcode и каждый сам вправе решить использовать ему подобный подход или нет и какие команды нужны и интересны. Остановлюсь на том, что показал куда смотреть.
Удобство выполнения команд еще в том, что есть привычный как в терминале переход к предыдущей/последующей команде через стрелки вверх-вниз.
В результате наша работа с отладчиком выглядит вот так:

Swift и obj-c! Что внутри объекта?

Не знаю как вам, но мне не повезло писать проект на Swift с нуля, а пришлось с выходом нового языка в огромном obj-c проекте все новые файлы писать на Swifte. Не пишу про все несостыковки, но основная проблема в отладке, наверное, в следующем:
Создаем простой класс модели на Swift

class TestObject: NSObject {     var name: String = "name"     var index: Int = 123 } 

Теперь пишем на obj-c простой массив с нашим классом:

NSArray *array = @[[[TestObject alloc] init],                  [[TestObject alloc] init],                  [[TestObject alloc] init]]; 

Теперь ставим брейкпоинт на следующей строчке и смотрим на то, что бы мы увидели в Variables:

image

Знакомо? А где же наши name и index
Теперь смотрим, что мы можем сами руками в консоле:

(lldb) p array (__NSArrayI *) $0 = 0x00007f9a1878c6b0 @"3 objects" (lldb) po array <__NSArrayI 0x7f9a1878c6b0>( <CornerApp.TestObject: 0x7f9a1870fc60>, <CornerApp.TestObject: 0x7f9a1878c650>, <CornerApp.TestObject: 0x7f9a1878c680> )  (lldb) po [array debugDescription] <__NSArrayI 0x7f9a1878c6b0>( <CornerApp.TestObject: 0x7f9a1870fc60>, <CornerApp.TestObject: 0x7f9a1878c650>, <CornerApp.TestObject: 0x7f9a1878c680> )  (lldb) po array[0] <CornerApp.TestObject: 0x7f9a1870fc60>  (lldb) po array[0].name error: property 'name' not found on object of type 'id' error: 1 errors parsing expression (lldb) po [array[0] name] name  (lldb)  

p — это print
po — это print object, кидает объекту сообщение description, обратите внимание, что объектам еще можно послать сообщение debugDescription
po array[0].name — не работат, потому что для отладчика нулевой элемент в массиве типа id. А вот посылка сообщения name (po [array[0] name]) прекрасно работает. Не забываем, про то, что obj-c — это message oriented язык программирования.

Найди меня

Следующий кейс: у нас есть API — мы ходим на сервер за списком стран, потом преобразуем их во внутреннюю логику и где-то храним. Например, наша modelView идет за данными в хранилище и ищет модельку в словаре по ключу:

- (CACountry *)countryByCode:(NSString *)code {     return  [_countries objectForKey:code]; } 

Что мы видим в Variables? 242 страны, отлично, как нам быстро найти Россию?
image

Знаю, вот так:

(lldb) po [_countries objectForKey:@"RU"] <CACountry: 0x7fae00630ba0>  (lldb) po [[[_countries objectForKey:@"RU"] title] string] Россия 

И хочу заметить, что в строке отладчика прекрасно работает автодополнение для посылки сообщений в objective-c

Магические UIView

Наверняка, если делали какой-нибудь нетривиальный UI были ситуации, когда что-нибудь в интерфейсе съехало и нужно разобраться, кто виноват. При это перезапуск приложение грозит тем, что может не удастся воспроизвести ситуацию. Вот тут-то работа с отладчиком незаменима.
В результате выполнения простого

(lldb) po self.view.subviews 

увидим что-то вроде такого:

<UICollectionView: 0x7fd9fb81a200; frame = (0 0; 375 667); clipsToBounds = YES; opaque = NO; autoresize = W+H; gestureRecognizers = <NSArray: 0x7fd9fb416cb0>; layer = <CALayer: 0x7fd9fb4067f0>; contentOffset: {0, -180}; contentSize: {375, 843}> collection view layout: <TGLStackedLayout: 0x7fd9fb410000>, <CASortView: 0x7fd9fb4468c0; frame = (67.5 607; 240 60); clipsToBounds = YES; alpha = 0; opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x7fd9fb481290>>, <UIView: 0x7fd9fb47e240; frame = (0 0; 375 687); alpha = 0; layer = <CALayer: 0x7fd9fb4895a0>> 

Сразу видим набор view, их фреймы, свойства, всё то, что запихнули разработчики Apple в стандартное сообщение description.
Если нужно легко можем уточнить какое-нибудь свойство, можно использовать адрес в памяти напрямую:

(lldb) po [0x7fae2b792ba0 backgroundColor] UIDeviceWhiteColorSpace 0 1 

А вот вам напоследок кусок кода, который может доставать по иерархии все view заданного класса, может покажется полезным. Сам использую.

import UIKit  extension UIView {          func debugAllSubviewsOfClass(cls: AnyClass) -> [String] {         func goDeepAndPrint(inout views: [String], currentView: UIView) {             for v in currentView.debugSubview() {                 views.append(v.debugDescriptionWithParent())                 goDeepAndPrint(&views, currentView:v)             }         }         var views = [String]()         goDeepAndPrint(&views, currentView:self)         return views     }          func debugDescriptionWithParent() -> String {         let parentAddress = self.superview != nil ? String(format: "%p", self.superview!) : "nil"         return "\(self.description), parent = \(parentAddress)"     }          func debugSubview() -> [UIView] {         return self.subviews     } }  extension UITableView {          override func debugSubview() -> [UIView] {         return self.subviews + self.visibleCells     } }  extension UICollectionView {          override func debugSubview() -> [UIView] {         return self.subviews + self.visibleCells()     } } 

Summary

  • С отладчиком можно работать не только через кнопочки Xcode.
  • Как настроить открытие консоли при отладке в новой вкладке? Самое начало — большая картинка
  • Быстрый переход в консоль — Cmd + Shift + C,
    свернуть/развернуть консоль — Cmd + Shift + Y
  • Наши новые команды:
    Step Intostep или коротко s
    Step Overnext или коротко n
    Continuecontinue или c
    Отключить все брейкпоинты — breakpoint disable
    Вот тут весь список команд LLDB.
  • Obj-c не видит свойства объекта Swift-ового класса? Не беда:
    (lldb) po [array[0] name] 
  • В окне variables 242 ключа у словаря?
    Ищем быстро:
    (lldb) po [[[_countries objectForKey:@"RU"] title] string] Россия 
  • Облегчаем себе отладку UIView — чуть выше summary есть немного полезного кода

Всем спасибо, кто прочитал. Надеюсь пару приёмчиков кто-то для себя новых открыл.
Описал каждую из возможностей не подробно, но старался показать направление, кому нужно — может разобраться глубже или спросить в комментариях!

ссылка на оригинал статьи http://habrahabr.ru/post/269095/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *