你可能注意到在你的开发者生涯中, 一遍又一遍的输入相同的东西真的很糟糕.如果你用的某个指令输入起来很麻烦, 没有原因表明你应该输入完整的指令.仅仅是因为你学习了如何创建正则表达式断点, 你不得不疯狂的输入Swift
函数的完整名字.
同样的观点在LLDB中可以被应用到所有的指令上, 设置上, 或者代码执行上.然而, 截至目前这里有两个问题尚未解决:为你的指令创建快捷键并保存它们!你每次运行一个新的LLDB会话的时候, 你之前执行的所有的命令都会消失!
在本章中, 你将学习如何让通过.lldbinit
文件持久化这些命令.通过持久化你为自己创建方便的命令, 你的调试过程将会变得很流畅和高效. 这同样是一个很重要的概念因为从现在起, 你将会定期使用.lldbinit
文件.
如何持久化?
当LLDB被调用的时候, 它会搜索几个目录下面指定的初始化文件.如果找到了, 当LLDB启动之后在附加到指定进程之前这些文件就会被加载到LLDB中(如果你想在初始化的时候执行任何代码你就必须知道这一点). 你可以使用这些文件指定一些设置或者创建自定义的调试命令.
LLDB会在下面几个地方查找初始化文件:
-
~/.lldbinit-[context]
[context]位置的内容是Xcode, 如果你使用Xcode调试, 或者是lldb如果你通过命令行用LLDB调试.例如, 如果你在终端中调试但是希望命令只在LLDB中生效, 你需要加上的内容是~/.lldbinit-lldb
, 同时如果你希望命令只在Xcode中生效你需要使用~/.lldbinit-Xcode
. - 接下来, LLDB会搜索在
~/.lldbinit
中找到的内容. 这里是处理大多数逻辑的理想文件, 如果你想在终端中或者Xcode中使用LLDB命令的话. - 最后, LLDB会搜索它被调用时所在的目录. 不幸的是, 当Xcode调起LLDB的时候, 会在
/
目录下调起. 这里并不是放置.lldbinit
文件的理想位置, 所以本书中会忽略这种特殊的实现方式.
创建.lldbinit
文件
在本章中你将会创建你的第一个.lldbinit
文件.
首先, 打开终端并输入:
nano ~/.lldbinit
这会用nano
编辑器打开你的 .lldbinit
文件. 如果在那个位置上已经存在了那个文件, nano
会打开.lldbinit
文件而不是创建一个新的文件.
注意:你真的应该使用一些类似`vi`和`emacs`的编辑器编辑`.lldbinit`, 然后愤怒的在博客中写出其他编辑器是如何标新立异的. 我建议使用`nano`是经过深思熟虑的
在nano
编辑器中打开了以后, 在.lldbinit
文件的末尾加上下面的代码:
command alias -- Yay_Autolayout expression -l objc -O --
[[[[[UIApplication sharedApplication] keyWindow] rootViewController]
view] recursiveDescription]
这些代码的作用是创建了一个别名 - 也就是一个长表达式命令的缩写. 这个别名的名字叫做Yay_Autolayout
并且它会执行一个表达式命令获取根部的UIView
(只在iOS中生效)并且会提取出root view的所有子视图的位置并重新布局所有的子视图.
按下Ctrl + O
来保存你的更改, 但是现在还没有退出nano
.
打开Signals
的Xcode项目, 构建并运行Signals
程序. 在运行起来之后, 停止执行并在调试器中输入别名:
(lldb) Yay_Autolayout
这将会提取出应用程序中所有的视图!很兴奋吧!
注意:这条很酷的命令, 无论你是否拥有APP的源代码都会执行的做的同样好.你可以将LLDB附加到模拟器的`SpringBoard`并通过执行同样的方法提取出所有的视图.
现在, 试着获取这条新命令的帮助:
(lldb) help Yay_Autolayout
输出的内容看起来有点无聊. 你可以做的更好一点. 回到nano
的终端中,并在命令的别名里添加一些有用的信息. 就像下面这个样子:
command alias -H "Yay_Autolayout will get the root view and recursively
dump all the subviews and their frames" -h "Recursively dump views" --
Yay_Autolayout expression -l objc -O -- [[[[[UIApplication
sharedApplication] keyWindow] rootViewController] view]
recursiveDescription]
通过按下Ctrl + O
来保存这个文件.接下来构建并运行Signals
项目.
现在当你停下调试器并输入help Yay_Autolayout
, 你就会在底部看到这些帮助文字的输出. 可以通过-H
命令查看. 你也可以仅仅通过输入help
来获取一些简介, 它会给出-h
和其他一些命令的描述.
现在这些看起来可能毫无意义, 但是当你在.lldbinit
文件中有许多许多自定义命令的时候, 你就会感谢你为自己提供的文档.
带参数命令的别名
你刚才只是创建了不需要任何参数的单行命令的别名.然而, 你经常会需要创建一些支持输入的命令的别名.
返回到打开nano
的终端窗口中. 在文件的底部输入下面的内容:
command alias cpo expression -l objc -O --
你就创建了一个叫做cpo
的新命令. cpo
会做一个普通的po
(print object)的操作, 但是它会使用Objective-C的环境. 当你在Swift环境中但是你却想打印出一个内存地址或者一个寄存器或者你知道的可用的Objective-C对象的时候这是一个理想的命令.
保存你在nano
中做出的更改, 然后返回到Signals
项目中. 找到MasterViewController
的viewDidLoad
然后在这个函数顶部创建一个断点.构建并运行应用程序:
理解
cpo
命令的重要性的最佳方式是, 首先获取MasterViewController
的引用.
(lldb) po self
你将会得到类似下面的输出:
取出输出内容末尾的内存地址(通常情况下, 你得到的内存地址用上面的有所不同), 并尝试在调试器中打印出来:
(lldb) po 0x7fc8295071a0
这不会产生任何有意义的输出内容, 因为你停留在了Swift文件中, 而Swift是一种类型安全的语言. 简单的在swift中打印内存地址是捕获做任何事的. 这就是为什么Objective-C环境在调试的时候如此有用的原因, 特别是在处理只有一个内存地址的汇编代码的时候.
现在, 将你刚才创建的的新命令应用到地址上:
(lldb) cpo 0x7fc8295071a0
你会看到与你执行po self
命令是同样的输出:
这是一个获取对象的description
的有用命令, 无论对象是用Objective-C创建的还是使用swift创建的.
这是一个很勉强的例子, 因为你只会使用po self
命令.然而, 如果你只有一个内存地址, 然后你又不能用po
, 这个时候cpo
就派上用场了.
我们为什么要学这些?
你已经学习了如何穿件简单命令的别名并将他们保存在.lldbinit
文件中. 这即能在Xcode的LLDB中生效也能在终端的LLDB中生效.
作为一个练习, 在~/.lldbinit
文件总中加你新创建的cpo
命令的帮助信息, 以便当你创建了许多自定义命令之后你依然可以想起来这个命令的作用.记住-h
选项是简洁的帮助信息, 而-H
选项是比较长的帮助信息.记得使用--
分割出你要输入到命令中的参数.
此外, 为你常用的命令创建一个别名.把这个别名放到~/.lldbinit
文件中, 并使用它!