如何在 Swift 的 LLDB 控制台中使用 _printHierarchy?
How to use _printHierarchy in LLDB console with Swift?
Apple 添加了一个可以在 LLDB 控制台中使用的 private helper _printHierarchy
in iOS8:
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 中打印出视图控制器层次结构? 或者有没有办法在 LLDB 控制台中使用 ObjC,即使在 Swift 个项目?
似乎,因为这是一个 "private" 助手,所以它不会暴露给 Swift。它也无法从 Objective-C 内访问,即
UIViewController* vc = // Assign view controller
[vc _printHierarchy];
导致编译时错误。但是,可能有效的方法是在桥接头内使用 NSSelectorFromString
,例如
-(void) printHierarchyWithVC:(UIViewController*) vc
{
[vc performSelector: NSSelectorFromString(@"_printHierarchy")];
}
定义后,您可以从 Swift 调用 printHierarchyWithVC
。
经过一些研究,我发现只需在项目的桥接 header 中公开这个特定的 API(从 iOS runtime headers 复制),以便 Swift:
@interface UIViewController (Debugging)
+ (id)_printHierarchy;
@end
在运行时,这个class方法可以按如下方式调用:
(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) 线程上调用。
您指出如何显示视图控制器层次结构:
po [[[UIWindow keyWindow] rootViewController] _printHierarchy]
然后你说:
This works only if you are debugging code on Objective C. In Swift, however, this doesn't work.
实际上,这在一定程度上取决于您暂停执行 Swift 程序的方式。问题是 expression
命令(po
使用)将在 Swift 帧中使用 Swift 表达式,在 Objective-C 帧中使用 Objective-C 表达式.因此,这意味着 po
行为会根据执行暂停的方式而变化:
例如,您可以在应用处于 运行:
时按 "pause" 按钮
如果这样做,您将能够将上述 po
语法与 Objective-C 表达式一起使用而不会发生意外。
另一方面,如果您在 Swift 代码中设置了一个断点,当您到达 [=16] 时,您将处于 Swift 帧=]提示。但是你可以明确地告诉 expression
命令你想使用带有 -l
(或 --language
)选项的 Objective-C 语法:
expr -l objc++ -O -- [[[UIWindow keyWindow] rootViewController] _printHierarchy]
在 WWDC 2014 视频 Advanced Swift Debugging in LLDB 中讨论了在 expr
命令中指定语言的能力。
如果您在 Swift 代码中停止,请将此行粘贴到调试器控制台(在 (lldb)
提示符之后)并按 enter 键以打印根视图控制器的层次结构:
po UIWindow.value(forKeyPath: "keyWindow.rootViewController._printHierarchy")!
如果您在 Objective-C 代码或汇编代码中停止,请改用此行:
po [UIWindow valueForKeyPath:@"keyWindow.rootViewController._printHierarchy"]
我建议在控制台中使用 expr -l objc++ -O -- [UIViewController _printHierarchy]
,因为它会以文本形式打印出完整的视图层次结构,我发现这比 UIWindow.valueForKeyPath 有用得多...请注意,您不需要要添加 po
以打印层次结构,请按原样使用。
这对我有用,Xcode 8 / swift 3 虽然我认为相同的命令也适用于 Xcode 的早期版本,因为它看起来像 objective 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>
安装后Chisel you can simply do the following and more:
(lldb) pvc
这里已经发布的选项很棒,另一个选项(类似于 ),如果你绝对需要使用 Swift lldb 上下文(意味着你不想通过 -l objc
,你可以调用 performSelector:
像这样桥接到 Swift:
func perform(_ aSelector: Selector!) -> Unmanaged<AnyObject>!
对于这种情况,您可以这样称呼它:
po UIApplication.shared.keyWindow!.rootViewController!.perform("_printHierarchy")!.takeUnretainedValue()
Apple 添加了一个可以在 LLDB 控制台中使用的 private helper _printHierarchy
in iOS8:
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 中打印出视图控制器层次结构? 或者有没有办法在 LLDB 控制台中使用 ObjC,即使在 Swift 个项目?
似乎,因为这是一个 "private" 助手,所以它不会暴露给 Swift。它也无法从 Objective-C 内访问,即
UIViewController* vc = // Assign view controller
[vc _printHierarchy];
导致编译时错误。但是,可能有效的方法是在桥接头内使用 NSSelectorFromString
,例如
-(void) printHierarchyWithVC:(UIViewController*) vc
{
[vc performSelector: NSSelectorFromString(@"_printHierarchy")];
}
定义后,您可以从 Swift 调用 printHierarchyWithVC
。
经过一些研究,我发现只需在项目的桥接 header 中公开这个特定的 API(从 iOS runtime headers 复制),以便 Swift:
@interface UIViewController (Debugging)
+ (id)_printHierarchy;
@end
在运行时,这个class方法可以按如下方式调用:
(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) 线程上调用。
您指出如何显示视图控制器层次结构:
po [[[UIWindow keyWindow] rootViewController] _printHierarchy]
然后你说:
This works only if you are debugging code on Objective C. In Swift, however, this doesn't work.
实际上,这在一定程度上取决于您暂停执行 Swift 程序的方式。问题是 expression
命令(po
使用)将在 Swift 帧中使用 Swift 表达式,在 Objective-C 帧中使用 Objective-C 表达式.因此,这意味着 po
行为会根据执行暂停的方式而变化:
例如,您可以在应用处于 运行:
时按 "pause" 按钮如果这样做,您将能够将上述
po
语法与 Objective-C 表达式一起使用而不会发生意外。另一方面,如果您在 Swift 代码中设置了一个断点,当您到达 [=16] 时,您将处于 Swift 帧=]提示。但是你可以明确地告诉
expression
命令你想使用带有-l
(或--language
)选项的 Objective-C 语法:expr -l objc++ -O -- [[[UIWindow keyWindow] rootViewController] _printHierarchy]
在 WWDC 2014 视频 Advanced Swift Debugging in LLDB 中讨论了在 expr
命令中指定语言的能力。
如果您在 Swift 代码中停止,请将此行粘贴到调试器控制台(在 (lldb)
提示符之后)并按 enter 键以打印根视图控制器的层次结构:
po UIWindow.value(forKeyPath: "keyWindow.rootViewController._printHierarchy")!
如果您在 Objective-C 代码或汇编代码中停止,请改用此行:
po [UIWindow valueForKeyPath:@"keyWindow.rootViewController._printHierarchy"]
我建议在控制台中使用 expr -l objc++ -O -- [UIViewController _printHierarchy]
,因为它会以文本形式打印出完整的视图层次结构,我发现这比 UIWindow.valueForKeyPath 有用得多...请注意,您不需要要添加 po
以打印层次结构,请按原样使用。
这对我有用,Xcode 8 / swift 3 虽然我认为相同的命令也适用于 Xcode 的早期版本,因为它看起来像 objective 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>
安装后Chisel you can simply do the following and more:
(lldb) pvc
这里已经发布的选项很棒,另一个选项(类似于 -l objc
,你可以调用 performSelector:
像这样桥接到 Swift:
func perform(_ aSelector: Selector!) -> Unmanaged<AnyObject>!
对于这种情况,您可以这样称呼它:
po UIApplication.shared.keyWindow!.rootViewController!.perform("_printHierarchy")!.takeUnretainedValue()