美文网首页iOS 宝典
UI 调试利器 Chisel 的使用

UI 调试利器 Chisel 的使用

作者: 貘鸣 | 来源:发表于2017-10-04 21:30 被阅读176次

    参考文章1: 调试器的妙用

    LLDB调试器和GDB调试器命令映射表

    1 什么是 Chisel

    Chisel 是一个 LLDB 命令集, 用于辅助 iOS 程序的调试. 它的 Github 主页在这里.

    要了解 Chisel, 首先需要了解什么是 LLDB. 如果已经了解过相关知识, 请直接跳过 LLDB 相关内容.

    2 LLDB 简介

    众所周知, 一个典型的bug修复周期包括: 修改代码—>编译—>运行, 然后祈祷结果是对的. 其实还有更快和更简单的办法, 就是利用 LLDB, 但千万不要只把它当做值的观察器来使用!

    LLDB 是一个开源调试器, 其特点是: 实现了一个"读取-求值-输出""循环, 并且拥有众多的 C++ 和 Python 插件, 并且已经被集成到了 Xcode 中.

    LLDB 有众多的插件, 而 Chisel 就是它的一个利用 Python 语言写的插件集, 下面先来看看 LLDB 的基本使用.

    3 LLDB 的基本使用

    首先来看如下的代码, 当触发断点后, LLDB 命令行输入会出现在界面的左下角:

    设置断点

    下面就来看看一些 LLDB 的基本命令的使用.

    3.1 help 命令

    最简单的命令是 help, 它会列出 LLDB 的帮助信息, 如果忘记某个命令的用法, 则可以使用 help <command> 来打印该命令的详细信息, 比如 help print 或者 help thread. 当然也可以这样用 help help 😝.

    3.2 print 命令

    有时希望打印出某个变量的值, 则可以使用 print 命令:

    使用 print 命令

    由于 LLDB 会自动进行命令前缀匹配, 则实际上该命令还可以这样写: prin, prip. 但不能使用 pr, 因为它和 process 命令冲突.(LLDB 中规定 p 是对应 print, 所以请放心使用)

    另外结果被自动放入了一个 $0 变量中, 所以接下来还可以使用该变量. 比如 print $0 + 9, 则会打印 108.

    在 LLDB 中, 任何以 $ 符号开头的变量都可以在接下来的命令中进行使用.

    3.3 expression 命令

    调试的时候, 有时候想直接为某个变量赋值, 然后观察后续代码的行为. 此时就可以用 expression 命令来改变某个变量的值. 注意, 这里改变的并非 LLDB 中变量的值, 而是运行程序中变量的值!

    下面就来使用 pe (分别是 printexpression 的缩写)做些事情.

    3.4 print 命令详解

    来看这条 LLDB 语句: p count = 18. 这条语句会直接改变 count 的值并打印出来. 如果使用 help print, 就会发现如下输出:

    'print' is an abbreviation for 'expression --'.
    

    其中的"两道杠"是用来分隔标志位和表达式.

    假设实际使用时输入 e -h +17, 此时无法分辨到底 -h 指的是标志位, 还是说变量 h 的相反数. 所以需要"两道杠"分隔符, 这样就很容易分辨了:

    e -h -- +17 中 -h 指的是标志位
    e -- -h +17 中 -h 指的是h的相反数
    

    所以 printexpression 无标志位时候的缩写.

    3.5 打印程序中的对象

    我们调试的时候想打印一个对象, 但是直接执行 p object 的话, 结果往往是😂:

    (NSString *) $7 = 0x0000000104da4040 @"red balloons"
    

    甚至是😂:

    (lldb) p @[ @"foo", @"bar" ]
    
    (NSArray *) $8 = 0x00007fdb9b71b3e0 @"2 objects"
    

    实际上我们只想打印该对象的 description 方法所输出的内容, 这时可以使用 -O 标志:

    (lldb) e -O -- $8
    <__NSArrayI 0x7fdb9b71b3e0>(
    foo,
    bar
    )
    

    清爽了好多. 该命令的缩写是 po (print object), 使用 po 的效果和上面一模一样:

    (lldb) po $8
    <__NSArrayI 0x7fdb9b71b3e0>(
    foo,
    bar
    )
    

    3.6 按指定形式打印

    使用 print 打印时数字, 默认是10进制:

    (lldb) p 16
    16
    

    可以使用 print/<fmt> 指定打印的形式, 比如 x 表示十六进制:

    (lldb) p/x 16
    0x10
    

    使用 t 表示二进制:

    (lldb) p/t 16
    0b00000000000000000000000000010000
    (lldb) p/t (char)16
    0b00010000
    

    另外 p/c 表示字符(character), p/s 表示字符串(string, 即以 \0 结尾的 char*).

    这里是所有可选的输出形式参数列表.

    3.7 变量

    LLDB 中的变量必须以 $ 符号开头, 使用 LLDB 变量, 可以简化很多操作:

    (lldb) e int $a = 2
    (lldb) p $a * 19
    38
    (lldb) e NSArray *$array = @[ @"Saturday", @"Sunday", @"Monday" ]
    (lldb) p [$array count]
    2
    (lldb) po [[$array objectAtIndex:0] uppercaseString]
    SATURDAY
    (lldb) p [[$array objectAtIndex:$a] characterAtIndex:0]
    error: no known method '-characterAtIndex:'; cast the message send to the method's return type
    error: 1 errors parsing expression
    

    上述代码中 LLDB 无法区分变量类型, 所以需要给它一些提示:

    (lldb) p (char)[[$array objectAtIndex:$a] characterAtIndex:0]
    'M'
    (lldb) p/d (char)[[$array objectAtIndex:$a] characterAtIndex:0]
    77
    

    3.8 流程控制

    可以使用 LLDB 命令来控制调试流程, 调试过程中的流程控制命令有如下四个:

    • 继续: c
    • 单步: n
    • 单步进入: s
    • 单步退出: finish

    讲了这么多关于 LLDB 的内容, 下面就来看如何安装和使用 Chisel.

    4 安装 Chisel

    可以使用 HomeBrew 来安装 Chisel, 根据命令行提示完成安装即可:

    brew update
    brew install chisel
    

    也可以直接下载 Chisel, 然后添加 ~/.lldbinit 文件, 内容如下:

    # ~/.lldbinit
    ...
    command script import /path/to/fblldb.py
    

    注意, 写这个文档的时候, chisel是1.5.0版本, 用 homebrew 安装完成后, 有可能 .lldbinit 文件并没有被创建, 导致在 Xcode 中无法使用 chisel. 故需要在用户根目录手动创建 .lldbinit 文件, 并添加文件内容:

    command script import /usr/local/opt/chisel/libexec/fblldb.py
    

    这里该 fblldb.py 文件的位置可能有所不同, 需要根据情况修改.(该文件路径在安装 chisel 的时候会有提示)

    安装完成后, 再次启动 Xcode 时, 就可以在 LLDB 中使用 Chisel 中的所有命令了.

    5 如何使用 Chisel

    由于 Chisel 是一个 LLDB 命令集插件, 故使用时直接调用其提供的命令即可.

    要查询所有命令, 可以在 LLDB 中执行help, 或查看 Chisel 官方命令列表: 完整命令列表.

    (lldb) help
    The following is a list of built-in, permanent debugger commands:
    ...
    
    The following is a list of your current user-defined commands:
    ...
    

    下面是一些常用命令:

    命令 含义 iOS OS X
    pviews Print the recursive view description for the key window. Yes Yes
    pvc Print the recursive view controller description for the key window. Yes No
    visualize Open a UIImage, CGImageRef, UIView, CALayer, NSData (of an image), UIColor, CIColor, or CGColorRef in Preview.app on your Mac. Yes No
    fv Find a view in the hierarchy whose class name matches the provided regex. Yes No
    fvc Find a view controller in the hierarchy whose class name matches the provided regex. Yes No
    show/hide Show or hide the given view or layer. You don't even have to continue the process to see the changes! Yes Yes
    mask/unmask Overlay a view or layer with a transparent rectangle to visualize where it is. Yes No
    border/unborder Add a border to a view or layer to visualize where it is. Yes Yes
    caflush Flush the render server (equivalent to a "repaint" if no animations are in-flight). Yes Yes
    bmessage Set a symbolic breakpoint on the method of a class or the method of an instance without worrying which class in the hierarchy actually implements the method. Yes Yes
    wivar Set a watchpoint on an instance variable of an object. Yes Yes
    presponder Print the responder chain starting from the given object. Yes Yes
    ... ... and many more!

    Chisel 特别在界面调试上有很大用途. 下面仅挑常用的说, 详细文档请移步这里.

    • show 和 hide:

      比如界面上有一个label, 变量名为 textLabel, 要显示它, 则使用:

      show textLabel
      

      要隐藏, 则使用:

      hide textLabel
      
    • alamborder 和 alamunborder:

      alamborder 可以将界面上存在歧义布局(ambiguous layout)的视图标注出来, 比如标注为红色边框, 边框线宽为2, 则使用(如果指定参数, 则:

      alamborder --color=red --width=2
      

      要取消, 则使用:

      alamunborder
      
    • border 和 unborder

      将一个视图用线框包裹起来:

       border xyzLabel
       unborder //隐藏
      

      执行这个命令, 它会使用默认配置: 即红色边框将该视图的矩形框标识出来. 当然也可以指定颜色线宽和其他参数.

    • dismiss

      将使用 present: 方法显示出来的 VC 取消显示:

       dismiss xxxViewController
      
    • flicker

      当看到一个视图变量, 但在代码中找很久都找不到它到底对应界面上什么东西的时候, 这个命令就可以出来帮忙了, 比如有一个 xyzLabel 变量, 你只知道它是个label 😂, 但如何找到它呢? 就使用如下方法, 这时 xyzLabel 会在界面上快速显隐两次, 这样就知道它在哪里了:

      flicker xyzLabel

    • mwarning

      模拟一个内存警告.

    • pactions

      打印某个 control 的所有 action 和 target:

        pactions xxxButton
      
    • pbcopy

      打印指定对象, 且将输出拷贝到剪贴板.

    • pbundlepath

      打印应用程序的 bundle 目录的地址.

    • pclass

      打印某目标对象的类型继承链:

       (lldb) pclass self
       HomeBoard_iPhone
          | BaseRootBoard_iPhone
          |    | BeeUIBoard
          |    |    | UIViewController
          |    |    |    | UIResponder
          |    |    |    |    | NSObject
      
    • pdata

      将某 NSData 中的内容以字符串的形式打印出来, 并且可以指定编码, 默认是 utf-8:

      pdata valueData
      
    • pdocspath

      打印本应用的 Documents 目录路径.

    • pinternals

      打印某对象的内部情况:

      (lldb) pinternals valueData
      (_NSInlineData) $705 = {
        NSData = {
          NSObject = {
            isa = _NSInlineData
          }
        }
        _length = 9
      }
      
    • pjson

      以 JSON 格式打印字典或数组的内容:

      (lldb) pjson @[1, 2, 3]
      [
        1,
        2,
        3
      ]
      
    • pmethods

      打印指定类或对象的所有类方法和对象方法.

    • pproperties

      打印某对象或类型的所有属性

    • vs

      在当前视图树中搜索指定的视图.

    相关文章

      网友评论

        本文标题:UI 调试利器 Chisel 的使用

        本文链接:https://www.haomeiwen.com/subject/mkocyxtx.html