Как использовать _printHierarchy в консоли LLDB с Swift?
Apple добавила закрытый помощник _printHierarchy
в iOS8, который можно использовать в консоли LLDB:
po [[[UIWindow keyWindow] rootViewController] _printHierarchy]
который печатает всю иерархию контроллера представления в текстовой форме.
Это работает, только если вы отлаживаете код на Objective C. В Swift это не работает:
(lldb) po [[[UIWindow keyWindow] rootViewController] _printHierarchy]
error: <EXPR>:1:13: error: expected ',' separator
[[[UIWindow keyWindow] rootViewController] _printHierarchy]
^
,
<EXPR>:1:24: error: expected ',' separator
[[[UIWindow keyWindow] rootViewController] _printHierarchy]
^
,
<EXPR>:1:44: error: expected ',' separator
[[[UIWindow keyWindow] rootViewController] _printHierarchy]
^
,
Эквивалентное использование в Swift также не работает:
po UIApplication.sharedApplication().keyWindow!.rootViewController!._printHierarchy
заканчивается ошибкой (возможно, потому что _printHierarchy
является частным свойством):
(lldb) po UIApplication.sharedApplication().keyWindow!.rootViewController!._printHierarchy()
error: <EXPR>:1:64: error: 'UIViewController' does not have a member named '_printHierarchy'
UIApplication.sharedApplication().keyWindow!.rootViewController!._printHierarchy
^ ~~~~~~~~~~~~~~~
Вопрос: Как распечатать иерархию диспетчера представлений в Swift? Или есть способ использования ObjC в консоли LLDB даже в проектах Swift?
Ответы
Ответ 1
Вы указываете, как отображается иерархия контроллера представления с помощью:
po [[[UIWindow keyWindow] rootViewController] _printHierarchy]
Затем вы говорите:
Это работает, только если вы отлаживаете код на Objective C. В Swift это не работает.
Собственно, это немного зависит от того, как вы приостанавливаете выполнение своей программы Swift. Проблема в том, что команда expression
(в которой используется po
) будет использовать выражения Swift в кадрах Swift и выражения Objective-C в кадрах Objective-C. Таким образом, это означает, что поведение po
варьируется в зависимости от того, как выполнение приостанавливается:
-
Вы можете, например, нажать кнопку "пауза" во время работы приложения:
![pause]()
Если вы сделаете это, вы сможете использовать приведенный выше синтаксис po
с выражением Objective-C без инцидентов.
-
Если, с другой стороны, вы устанавливаете точку останова внутри вашего кода Swift, вы попадете в рамку Swift, когда вы перейдете к приглашению (lldb)
. Но вы можете явно сообщить команде expression
, что вы хотите использовать синтаксис Objective-C с опцией -l
(или --language
):
expr -l objc++ -O -- [[[UIWindow keyWindow] rootViewController] _printHierarchy]
Эта возможность указать язык в команде expr
обсуждается в видео WWDC 2014 Расширенная Swift-отладка в LLDB.
Ответ 2
Если вы остановились в коде Swift, вставьте эту строку в консоль отладчика (после подсказки (lldb)
) и нажмите клавишу ввода, чтобы распечатать иерархию контроллера корневого представления:
po UIWindow.value(forKeyPath: "keyWindow.rootViewController._printHierarchy")!
Если вы остановились в коде Objective-C кода или кода сборки, используйте эту строку вместо:
po [UIWindow valueForKeyPath:@"keyWindow.rootViewController._printHierarchy"]
Ответ 3
Похоже, поскольку это помощник "private", он каким-то образом не подвергается воздействию Swift. Он также недоступен из Objective-C, т.е.
UIViewController* vc = // Assign view controller
[vc _printHierarchy];
приводит к ошибке времени компиляции. Однако то, что может работать, заключается в использовании NSSelectorFromString
внутри заголовка моста, например.
-(void) printHierarchyWithVC:(UIViewController*) vc
{
[vc performSelector: NSSelectorFromString(@"_printHierarchy")];
}
Как только это определено, вы можете вызвать printHierarchyWithVC
из Swift.
Ответ 4
После некоторых исследований я узнал, что это просто вопрос об экспорте этого конкретного API (скопированный из заголовки времени выполнения iOS) в заголовке проекта поэтому он становится доступным для Swift:
@interface UIViewController (Debugging)
+ (id)_printHierarchy;
@end
Во время выполнения этот метод класса можно вызвать следующим образом:
(lldb) po UIViewController._printHierarchy() as NSString
<UINavigationController 0x7f8a50733c70>, state: appearing, view: <UILayoutContainerView 0x7f8a5064def0>
| <MyApp.RootViewController 0x7f8a507341f0>, state: appearing, view: <UIView 0x7f8a5056d860> not in the window
... распечатать иерархию контроллера представления. Обратите внимание, что метод должен вызываться только в основном потоке (UI).
Ответ 5
Я рекомендую использовать expr -l objc++ -O -- [UIViewController _printHierarchy]
в консоли, так как он распечатает всю иерархию представлений в текстовой форме, которую я нашел значительно более полезной, чем UIWindow.valueForKeyPath... Обратите внимание: вам не нужно добавлять po
в напечатайте иерархию, просто используйте как есть.
Это работает для меня, Xcode 8/swift 3, хотя я думаю, что одна и та же команда также работает и в более ранних версиях Xcode, потому что она похожа на объективную C.
Пример вывода из этой команды:
(lldb) expr -l objc++ -O -- [UIViewController _printHierarchy]
<MyProject.SwipeController 0x102213590>, state: disappeared, view: <UIView 0x102239ff0> not in the window
+ <MyProject.CameraViewController 0x102215680>, state: disappeared, view: <UIView 0x102422fd0> not in the window, presented with: <_UIFullscreenPresentationController 0x102219c60>
| + <MyProject.MapViewController 0x102214820>, state: appeared, view: <UIView 0x10bf52fe0>, presented with: <_UIFullscreenPresentationController 0x10fd1f890>
| | | <MyProject.MapPlaceCollectionViewController 0x10bf54680>, state: appeared, view: <UICollectionViewControllerWrapperView 0x1022438d0>
Ответ 6
После установки Chisel вы можете просто сделать следующее и более:
(lldb) pvc
Ответ 7
Параметры, которые уже были опубликованы здесь, отличные, еще один вариант (похожий на этот ответ), если вам абсолютно необходимо использовать SWIFL-контекст lldb (что означает вы не хотите передавать -l objc
, вы можете позвонить performSelector:
который соединяется с Swift следующим образом:
func perform(_ aSelector: Selector!) -> Unmanaged<AnyObject>!
В этом случае вы бы назвали это следующим образом:
po UIApplication.shared.keyWindow!.rootViewController!.perform("_printHierarchy")!.takeUnretainedValue()