在前面的章節(jié)中, 你已經(jīng)學(xué)習(xí)了為命令創(chuàng)建別名并將它們保存在lldbinit
文件中.不幸的是, 命令別名有一些局限性.
用這種方法創(chuàng)建的別名如果你用來執(zhí)行靜態(tài)命令會表現(xiàn)的很好, 但是有時候你想在命令中輸入一些內(nèi)容以便得到一些有用的輸出.
用命令別名的本質(zhì)是用實(shí)際的命令替換了別名. 如果你想在命令的中間輸入一些東西, 比如一條獲取對象實(shí)例的類的命令, 提供需要輸入的對象?
一種極其拙劣的解決方案就是用命令別名做下面的事情(請永遠(yuǎn)不要這樣做):
(lldb) po id $INPUT = @"input test";
(lldb) command alias getcls po -l objc -O -- [$INPUT class]
這在LLDB中創(chuàng)建了一個叫做$INPUT
的臨時變量然后使用$INPUT
獲取類.但是這種方式很拙劣. 你不得不每次都要重新聲明一個$INPUT
, 這完全違背了使用快捷命令的初衷.
然而, 不要絕望 - 這里有一種優(yōu)雅的解決方案可以支持輸入.
命令正則
在LLDB中command regex
命令實(shí)際上很像命令別名, 除非你可以為輸入的將會作為命令的一部分執(zhí)行的內(nèi)容提供一個正則表達(dá)式.
command regex
帶來了一種輸入語法類似于下面的樣子:
s/<regex>/<subst>/
這是一個普通的正則表達(dá)式. 它是以s/
開頭的, 這指定了一個流編輯器將輸入的內(nèi)容作為替換命令.<regex>
是將要被替代的部分, <subst>
部分是用來替代的部分.
注意:這種語法衍生自`sed`終端命令. 知道這點(diǎn)是很重要的, 因?yàn)槿绻銍L試使用高級模式, 你可以查看`sed`的主頁來看看在替換格式語法中可能是哪些內(nèi)容.
到了看一個具體例子的時間了. 打開Signals
的Xcode項(xiàng)目. 構(gòu)建并運(yùn)行, 然后將應(yīng)用程序暫停在調(diào)試器中. 一旦LLDB的控制臺出現(xiàn)并準(zhǔn)備接受輸入, 在LLDB中輸入下面的內(nèi)容:
(lldb) command regex rlook 's/(.+)/image lookup -rn %1/'
你輸入的這條命令會使你的 image
正則搜索更簡單. 你已經(jīng)創(chuàng)建了一個叫做rlook
的新命令. 這條新命令會獲取rlook
后面的所有東西并用image lookup -rn
作為它的前綴. 這條命令做了這樣一件事情, 用image lookup -rn %1
替換可以匹配一個或多個字符的正則表達(dá)式(圓括號) 內(nèi)的所有內(nèi)容. %1
表示這匹配到的內(nèi)容.
所以, 例如, 你輸入了這樣的內(nèi)容:
rlook FOO
LLDB實(shí)際執(zhí)行的的是下面的內(nèi)容:
image lookup -rn FOO
現(xiàn)在你就可以通過僅僅輸入rlook
來替代輸入長長的image lookup -rn
命令.
但是等一下, 現(xiàn)在變得好一點(diǎn)了!假如與rl
字符沒有沖突, 你也可以用rl
字符替代. 你可以指定任何命令, 可以是內(nèi)置的或者你自己的, 只要是與其他命令沒有沖突的前綴.
這就意味著你可以使用更簡潔的輸入來搜索viewDidLoad
方法. 現(xiàn)在來試一下:
(lldb) rl viewDidLoad
這回提取出當(dāng)前可執(zhí)行文件中所有模塊中的viewDidLoad
的實(shí)現(xiàn). 嘗試將搜索范圍限定在viewDidLoad
APP中:
(lldb) rl viewDidLoad Signals
現(xiàn)在你已經(jīng)確信了這條命令, 講下面這行代碼添加到~/.lldbinit
文件中:
command regex rlook 's/(.+)/image lookup -rn %1/'
注意:實(shí)現(xiàn)一個正則命令的最佳方式是在程序運(yùn)行中用LLDB實(shí)現(xiàn). 這可以讓你在命令正則中重復(fù)聲明(在你對它不滿意的時候重新聲明)并且可以在不重啟LLDB的情況下測試它.如果你對命令很滿意, 那么將它添加到`~/.lldbinit`文件中以便LLDB每次啟動的時候都可以使用.
從現(xiàn)在開始rlook
就變得可用了, 你再也不用輸入令人頭疼的image lookup -rn
的完整命令了.太棒了!
執(zhí)行復(fù)雜的邏輯
是時候把命令的正則表達(dá)式提升一個等級了.實(shí)際上你可以用一條命令的別名來執(zhí)行多條指令. 現(xiàn)在LLDB應(yīng)該依然在暫停狀態(tài), 輸入下面這條新的命令:
(lldb) command regex -- tv 's/(.+)/expression -l objc -O -- @import
QuartzCore; [%1 setHidden:!(BOOL)[%1 isHidden]]; (void)[CATransaction
flush];/'
這是一條復(fù)雜而有用的命令, 將會創(chuàng)建一個名字叫tv
(toggle view)的命令, 可用與當(dāng)調(diào)試器暫停的時候在UIView (或者 NSView)之間切換.
這條命令由三行代碼組成:
-
@import QuartzCore
, 在調(diào)試器的地址空間中導(dǎo)入QuartzCore
框架. 這是必要的因?yàn)橹挥性谀懵暶髁舜a之后調(diào)試器才理解你在執(zhí)行什么. 你需要執(zhí)行QuartzCore
框架中的代碼, 之前還沒有導(dǎo)入, 但是現(xiàn)在已經(jīng)導(dǎo)入了. -
[%1 setHidden:!(BOOL)[%1 isHidden]];
, 在視圖的可見于不可見之間切換, 這取決于之前的狀態(tài)是可見的還是不可見的. 注意isHidden
不知道返回值的類型, 因此你需要將它轉(zhuǎn)換為Objective-C
中的Bool值. - 最后一行代碼是,
[CATransactionflush]
, 它會刷新CATransaction
隊(duì)列. 調(diào)試器中控制UI也就是通常所說的屏幕在調(diào)試器繼續(xù)執(zhí)行之前不會反映出任何改變. 然而, 這個方法將會更新LLDB中執(zhí)行的結(jié)果而不需要continue
就能展示視覺上的改變.
注意:由于輸入?yún)?shù)的限制, 多行輸入是不允許的, 所以你不得不將所有的指令都放在一行代碼中. 在手工控制正則命令的時候這很難看但卻很有必要.然而, 如果你曾經(jīng)在`Objective-C/Swift`的源代碼中做過這些, 那么蘋果上帝可能用很多次的審核嚴(yán)厲的懲罰過你!
如果LLDB依然在暫停狀態(tài), 執(zhí)行新創(chuàng)建的tv
命令:
(lldb) tv [[[UIApp keyWindow] rootViewController] view]
查看模擬器里確認(rèn)一下視圖已經(jīng)消失了.
現(xiàn)在只需要簡單的在LLDB中按下
Enter
, LLDB就會重復(fù)你之前執(zhí)行的最后一條指令.視圖會刷新到之前的狀態(tài).現(xiàn)在你已經(jīng)實(shí)現(xiàn)了tv的命令, 將它添加到
~/.lldbinit
文件中.
command regex -- tv 's/(.+)/expression -l objc -O -- @import QuartzCore;
[%1 setHidden:!(BOOL)[%1 isHidden]]; (void)[CATransaction flush];/'
鏈接正則輸入
這條命令之所以選擇使用怪誕的流編輯器輸入是有原因的:這種格式可以讓你輕松的在一條命令中指定多個動作. 當(dāng)給出多個指令的時候, 這個正則表達(dá)式會試著匹配每一個輸入.如果輸入匹配到了, 匹配到的內(nèi)容就會應(yīng)用到指令的<subst>
部分.如果輸入沒有匹配到指定的流, 他就會進(jìn)入下一條命令并且看看正則表達(dá)式是否能匹配到那些輸入.
當(dāng)我們在處理內(nèi)存中或者寄存器中的對象的時候使用Objective-C環(huán)境是普遍必要的. 因?yàn)? 以方括號[
開始的任何語句或者@
字符都是Objective-C里面的.這是因?yàn)镾wift在處理內(nèi)存數(shù)據(jù)的時候很難, 并且它不允許你訪問寄存器, 同時在處理swift表達(dá)式的時候也不是以[
或者@
開始的.
你可以使用這些信息自動檢測給定的輸入需要什么樣的環(huán)境.
讓我們看一下你應(yīng)該怎么去構(gòu)建一個獲取對象類信息的一條指令,:
? 在 Objective-C中, 你應(yīng)該使用[objcObject class]
.
? 在 Swift 中, 你應(yīng)該輸入 (of: swiftObject)
.
在Xcode中, 在MasterViewController.viewDidLoad () - > ()
處創(chuàng)建一個斷點(diǎn)(不要忘記中間的空格).
構(gòu)建并運(yùn)行, 等待斷點(diǎn)被觸發(fā).像往常一樣, 轉(zhuǎn)到調(diào)試器里.
首先, 構(gòu)建出新命令
getcls
的Objective-C的實(shí)現(xiàn).
(lldb) command regex getcls 's/(([0-9]|\$|\@|\[).*)/cpo [%1 class]/'
哇, 這條正則表達(dá)式讓我們的眼睛都花了.我們來拆解一下:
首先, 這里有一個內(nèi)部分組說在起始部分可以匹配接下來的字符:
? [0-9]
的意思是可以是0-9的數(shù)字.
? \$
的意思是$
的字面量將會被匹配
? \@
的意思是@
的字面量將會被匹配
? \[
的意思是[
的字面量將會被匹配
以上面的內(nèi)容為開始的字符都會生成一個匹配. 緊跟著的.*
的意思是為一個或多個字符產(chǎn)生一個匹配.
整體看來,這句話的意思是一個數(shù)字或者$
符號或者 @
符號或者[
符號, 后面跟著任何字符的內(nèi)容都會作為命令匹配到的結(jié)果并且運(yùn)行cpo [%1 class]
.再說一次, %1
會被正則表達(dá)式中第一個匹配到的內(nèi)容替換. 在這里, 就是整條命令. 而內(nèi)部匹配器(匹配一個數(shù)字, $, 等等)將會作為%2
.
現(xiàn)在用一組命令來實(shí)驗(yàn)一下getcls
命令看看它是如何工作的:
(lldb) getcls @"hello world"
__NSCFString
(lldb) getcls @[@"hello world"]
__NSSingleObjectArrayI
(lldb) getcls [UIDevice currentDevice]
UIDevice
(lldb) cpo [UIDevice currentDevice]
<UIDevice: 0x60800002b520>
(lldb) getcls 0x60800002b520
UIDevice
很神奇吧!
然而, 這只能處理Objective-C環(huán)境中用命令匹配到的引用.例如, 試一下下面的內(nèi)容:
(lldb) getcls self
你會得到一個錯誤:
error: getcls
這是因?yàn)檎齽t表達(dá)式?jīng)]有匹配到你輸入的內(nèi)容. 讓我們在getcls
中添加一個匹配其他形式的輸入. 現(xiàn)在在LLDB中輸入下面的內(nèi)容:
(lldb) command regex getcls 's/(([0-9]|\$|\@|\[).*)/cpo [%1 class]/' 's/
(.+)/expression -l swift -O -- type(of: %1)/'
這看起來更復(fù)雜了, 但還不算太糟. 這條命令的第一部分與你在前面添加的一致. 但是現(xiàn)在你在末尾添加了另外一個正則表達(dá)式. 這一個正則表達(dá)式可以捕獲所有輸入, 就像你添加的rlook
命令. 這里捕獲到的所有內(nèi)容的指令很簡單叫做type(of:)
用輸入的內(nèi)容作為參數(shù).
用self
再次執(zhí)行這條命令:
(lldb) getcls self
你會得到期望中的Signals.MasterViewController
輸出. 鑒于, 你是在Swift環(huán)境下匹配的內(nèi)容, 你可以用另外一種有趣的方法使用這條命令.
(lldb) getcls self .title
注意中間有一個空格, 而且這條命令依然起作用. 這是因?yàn)槟愀嬖Vswift環(huán)境獲取除了換行符以外的所有字符.
一旦, 你完成了練習(xí)并改進(jìn)了getcls
命令, 別忘了把它加到~/.lldbinit
文件中.
命令正則的局限性
LLDB中的命令正則在創(chuàng)造性應(yīng)用的數(shù)量上有些限制. LLDB的命令正則有一個你只能在<subst>
上應(yīng)用一個參數(shù)的限制. 這就意味著你那些支持多個參數(shù)的高級命令也逃不出這個限制.
幸運(yùn)的是, LLDB有script bridging
接口 - 一個完全由Python實(shí)現(xiàn)的在調(diào)試過程中用來實(shí)現(xiàn)高級LLDB指令的功能.在后面的章節(jié)中你會深入的學(xué)習(xí)script bridging
.
但是現(xiàn)在, 簡單的使用command alias
或者command regex
來滿足你調(diào)試的需要.
我們?yōu)槭裁匆獙W(xué)習(xí)這些?
回到本章中你已經(jīng)創(chuàng)建并添加了語法和幫助文檔的正則表達(dá)式命令.
正如前面提到的, 你將會感謝你自己提供了這些幫助文檔來提醒你之前創(chuàng)建的命令的功能.