【问题标题】:How to use _printHierarchy in LLDB console with Swift?如何在带有 Swift 的 LLDB 控制台中使用 _printHierarchy?
【发布时间】:2015-03-14 00:35:18
【问题描述】:

Apple 新增了一个private helper _printHierarchy in 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 中打印出视图控制器层次结构? 或者有没有办法在 LLDB 控制台中甚至在 Swift 项目中使用 ObjC?

【问题讨论】:

    标签: ios swift ios8 xcode6 lldb


    【解决方案1】:

    这里已经发布的选项很棒,另一个选项(类似于this answer),如果你绝对需要使用Swift lldb上下文(意味着你不想通过-l objc,是你可以拨打performSelector:

    像这样桥接到 Swift:

    func perform(_ aSelector: Selector!) -> Unmanaged<AnyObject>!
    

    对于这种情况,您可以这样称呼它:

    po UIApplication.shared.keyWindow!.rootViewController!.perform("_printHierarchy")!.takeUnretainedValue()
    

    【讨论】:

      【解决方案2】:

      如果您在 Swift 代码中停止,请将此行粘贴到调试器控制台(在(lldb) 提示符之后)并按 Enter 以打印根视图控制器的层次结构:

      po UIWindow.value(forKeyPath: "keyWindow.rootViewController._printHierarchy")!
      

      如果您在 Objective-C 代码或汇编代码中停止,请改用此行:

      po [UIWindow valueForKeyPath:@"keyWindow.rootViewController._printHierarchy"]
      

      【讨论】:

      • 你能在你的答案周围添加一些上下文吗?
      • 为什么?问题是“如何在带有 Swift 的 LLDB 控制台中使用 _printHierarchy?”答案就是我显示的命令。
      【解决方案3】:

      安装Chisel后,您可以简单地执行以下操作和more

      (lldb) pvc

      【讨论】:

        【解决方案4】:

        我建议在控制台中使用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>
        

        【讨论】:

          【解决方案5】:

          您指出如何显示视图控制器层次结构:

          po [[[UIWindow keyWindow] rootViewController] _printHierarchy]
          

          然后你说:

          这仅在您在 Objective C 上调试代码时才有效。但是,在 Swift 中,这不起作用。

          实际上,这在一定程度上取决于您如何暂停 Swift 程序的执行。问题是expression 命令(po 使用)将在 Swift 框架中使用 Swift 表达式,在 Objective-C 框架中使用 Objective-C 表达式。因此,这意味着 po 的行为取决于执行暂停的方式:

          • 例如,您可以在应用运行时按下“暂停”按钮:

            如果您这样做,您将能够毫无意外地将上述po 语法与Objective-C 表达式一起使用。

          • 另一方面,如果您在 Swift 代码中设置断点,当您到达 (lldb) 提示符时,您将处于 Swift 框架中。但是你可以通过-l(或--language)选项明确告诉expression 命令你想使用Objective-C 语法:

            expr -l objc++ -O -- [[[UIWindow keyWindow] rootViewController] _printHierarchy]
            

          在 WWDC 2014 视频 Advanced Swift Debugging in LLDB 中讨论了这种在 expr 命令中指定语言的能力。

          【讨论】:

          • 嗯,我已经尝试过了,我用控制台中的错误更新了我的问题。
          • @TomKraina 如果您在 Swift 代码中使用断点,那么您在 Swift 框架中,因此 po 需要 Swift 表达式。但是,如果您只是在 Swift 程序运行时按下暂停按钮(这是我经常做的),当您收到 (lldb) 提示时,您很可能不在 Swift 框架中,因此目标-C 表达式很好。幸运的是,如果您在 Swift 框架中暂停应用程序,您仍然可以显式提供 --language 选项(或 -l 选项)来指定即使您在 Swift 框架中,它也应该解释 Objective-C 表达式。请参阅修改后的答案。
          • 太酷了!我不知道您可以在 lldb 中指定语言!
          • 或者只是expr -l objc++ -O -- [UIViewController _printHierarchy]
          【解决方案6】:

          经过一番研究,我发现只需在项目的桥接头中公开这个特定的 API(复制自 iOS runtime headers),以便 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) 线程上调用。

          【讨论】:

            【解决方案7】:

            看起来,由于这是一个“私人”助手,它不会以某种方式暴露给 Swift。它也不能从 Objective-C 中访问,即

            UIViewController* vc = // Assign view controller
            [vc _printHierarchy];
            

            导致编译时错误。但是,可能有用的是在桥接头中使用NSSelectorFromString,例如

            -(void) printHierarchyWithVC:(UIViewController*) vc
            {
                [vc performSelector: NSSelectorFromString(@"_printHierarchy")];
            }
            

            一旦定义好,你就可以从 Swift 中调用printHierarchyWithVC

            【讨论】:

              猜你喜欢
              • 2018-08-12
              • 1970-01-01
              • 1970-01-01
              • 2019-11-03
              • 1970-01-01
              • 2013-02-15
              • 1970-01-01
              • 2021-09-28
              • 1970-01-01
              相关资源
              最近更新 更多