iOS開發日記58-Bug調試(lldb)

今天博主有一些Bug調試的需求,遇到了一些困難點,在此和你們分享,但願可以共同進步.javascript

Xcode的Bug調試方法大概有如下幾種:html

1.斷點,全局斷點,條件斷點配合Nslog找出Bugjava

2.靜態分析工具:Analyze,靜態檢測內存python

3.動態分析工具:Profile,其中最經常使用的是Leaks(檢測內存泄露)和Allocations(檢測內存溢出)git

4.gdb調試:Xcode4.0以前的調試工具github

5.lldb調試:Xcode4.0以後的調試工具shell

今天就和你們分享一下如何經過lldb,成爲Bug解決達人express

LLDB

LLDB 是一個有着 REPL 的特性和 C++ ,Python 插件的開源調試器。LLDB 綁定在 Xcode 內部,存在於主窗口底部的控制檯中。調試器容許你在程序運行的特定時暫停它,你能夠查看變量的值,執行自定的指令,而且按照你所認爲合適的步驟來操做程序的進展。小程序

與此同時,讓咱們以在調試器中打印變量來開始咱們的旅程吧。swift

基礎

這裏有一個簡單的小程序,它會打印一個字符串。注意斷點已經被加在第 8 行。斷點能夠經過點擊 Xcode 的源碼窗口的側邊槽進行建立。

程序會在這一行中止運行,而且控制檯會被打開,容許咱們和調試器交互。那咱們應該打些什麼呢?

help

最簡單命令是 help,它會列舉出全部的命令。若是你忘記了一個命令是作什麼的,或者想知道更多的話,你能夠經過 help <command> 來了解更多細節,例如 help print 或者 help thread。若是你甚至忘記了 help 命令是作什麼的,你能夠試試 help help。不過你若是知道這麼作,那就說明你大概尚未忘光這個命令。😛

print

打印值很簡單;只要試試 print 命令:

LLDB 實際上會做前綴匹配。因此你也可使用 prinpri,或者 p。但你不能使用 pr,由於 LLDB 不能消除和 process 的歧義 (幸運的是 p 並無歧義)。

你可能還注意到了,結果中有個 $0。實際上你可使用它來指向這個結果。試試 print $0 + 7,你會看到 106。任何以美圓符開頭的東西都是存在於 LLDB 的命名空間的,它們是爲了幫助你進行調試而存在的。

expression

若是想改變一個值怎麼辦?你或許會猜 modify。其實這時候咱們要用到的是 expression 這個方便的命令。

這不只會改變調試器中的值,實際上它改變了程序中的值。這時候繼續執行程序,將會打印 42 red balloons。神奇吧。

注意,從如今開始,咱們將會偷懶分別以 p 和 e 來代替 print 和 expression

什麼是 print 命令

考慮一個有意思的表達式:p count = 18。若是咱們運行這條命令,而後打印 count 的內容。咱們將看到它的結果與 expression count = 18 同樣。

和 expression 不一樣的是,print 命令不須要參數。好比 e -h +17 中,你很難區分究竟是以 -h 爲標識,僅僅執行 +17 呢,仍是要計算 17 和 h 的差值。連字符號確實很讓人困惑,你或許得不到本身想要的結果。

幸運的是,解決方案很簡單。用 -- 來表徵標識的結束,以及輸入的開始。若是想要 -h 做爲標識,就用 e -h -- +17,若是想計算它們的差值,就使用 e -- -h +17。由於通常來講不使用標識的狀況比較多,因此 e -- 就有了一個簡寫的方式,那就是 print

輸入 help print,而後向下滾動,你會發現:

'print' is an abbreviation for 'expression --'. (print是 `expression --` 的縮寫) 

打印對象

嘗試輸入

p objects

輸出會有點囉嗦

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

若是咱們嘗試打印結構更復雜的對象,結果甚至會更糟

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

實際上,咱們想看的是對象的 description 方法的結果。我麼須要使用 -O (字母 O,而不是數字 0) 標誌告訴 expression 命令以 對象 (Object) 的方式來打印結果。 

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

幸運的是,e -o -- 有也有個別名,那就是 po (print object 的縮寫),咱們可使用它來進行簡化:

(lldb) po $8 <__NSArrayI 0x7fdb9b71b3e0>( foo, bar ) (lldb) po @"lunar" lunar (lldb) p @"lunar" (NSString *) $13 = 0x00007fdb9d0003b0 @"lunar" 

打印變量

能夠給 print 指定不一樣的打印格式。它們都是以 print/<fmt> 或者簡化的 p/<fmt> 格式書寫。下面是一些例子:

默認的格式

(lldb) p 16
16

十六進制:

(lldb) p/x 16
0x10

二進制 (t 表明 two):

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

你也可使用 p/c 打印字符,或者 p/s 打印以空終止的字符串 (譯者注:以 '\0' 結尾的字符串)。 
這裏是格式的完整清單。

變量

如今你已經能夠打印對象和簡單類型,而且知道如何使用 expression 命令在調試器中修改它們了。如今讓咱們使用一些變量來減小輸入量。就像你能夠在 C 語言中用 int a = 0 來聲明一個變量同樣,你也能夠在 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 

變量使調試器變的容易使用得多,想不到吧?😉

流程控制

當你經過 Xcode 的源碼編輯器的側邊槽 (或者經過下面的方法) 插入一個斷點,程序到達斷點時會就會中止運行。

調試條上會出現四個你能夠用來控制程序的執行流程的按鈕。

從左到右,四個按鈕分別是:continue,step over,step into,step out。

第一個,continue 按鈕,會取消程序的暫停,容許程序正常執行 (要麼一直執行下去,要麼到達下一個斷點)。在 LLDB 中,你可使用 process continue 命令來達到一樣的效果,它的別名爲 continue,或者也能夠縮寫爲 c

第二個,step over 按鈕,會以黑盒的方式執行一行代碼。若是所在這行代碼是一個函數調用,那麼就不會跳進這個函數,而是會執行這個函數,而後繼續。LLDB 則可使用 thread step-overnext,或者 n 命令。

若是你確實想跳進一個函數調用來調試或者檢查程序的執行狀況,那就用第三個按鈕,step in,或者在LLDB中使用 thread step instep,或者 s 命令。注意,當前行不是函數調用時,next 和 step 效果是同樣的。

大多數人知道 cn 和 s,可是其實還有第四個按鈕,step out。若是你曾經不當心跳進一個函數,但實際上你想跳過它,常見的反應是重複的運行 n 直到函數返回。其實這種狀況,step out 按鈕是你的救世主。它會繼續執行到下一個返回語句 (直到一個堆棧幀結束) 而後再次中止。

例子

考慮下面一段程序:

假如咱們運行程序,讓它中止在斷點,而後執行下面一些列命令:

p i
n
s
p i
finish
p i
frame info

這裏,frame info 會告訴你當前的行數和源碼文件,以及其餘一些信息;查看 help framehelp thread 和 help process來得到更多信息。這一串命令的結果會是什麼?看答案以前請先想想。

(lldb) p i (int) $0 = 99 (lldb) n 2014-11-22 10:49:26.445 DebuggerDance[60182:4832768] 101 is odd! (lldb) s (lldb) p i (int) $2 = 110 (lldb) finish 2014-11-22 10:49:35.978 DebuggerDance[60182:4832768] 110 is even! (lldb) p i (int) $4 = 99 (lldb) frame info frame #0: 0x000000010a53bcd4 DebuggerDance`main + 68 at main.m:17 

它始終在 17 行的緣由是 finish 命令一直運行到 isEven() 函數的 return,而後馬上中止。注意即便它還在 17 行,其實這行已經被執行過了。

Thread Return

調試時,還有一個很棒的函數能夠用來控制程序流程:thread return 。它有一個可選參數,在執行時它會把可選參數加載進返回寄存器裏,而後馬上執行返回命令,跳出當前棧幀。這意味這函數剩餘的部分不會被執行。這會給 ARC 的引用計數形成一些問題,或者會使函數內的清理部分失效。可是在函數的開頭執行這個命令,是個很是好的隔離這個函數,僞造返回值的方式 。

讓咱們稍微修改一下上面代碼段並運行:

p i
s
thread return NO n p even0 frame info 

看答案前思考一下。下面是答案:

(lldb) p i (int) $0 = 99 (lldb) s (lldb) thread return NO (lldb) n (lldb) p even0 (BOOL) $2 = NO (lldb) frame info frame #0: 0x00000001009a5cc4 DebuggerDance`main + 52 at main.m:17 

斷點

咱們都把斷點做爲一箇中止程序運行,檢查當前狀態,追蹤 bug 的方式。可是若是咱們改變和斷點交互的方式,不少事情都變成可能。

斷點容許控制程序何時中止,而後容許命令的運行。

想象把斷點放在函數的開頭,而後用 thread return 命令重寫函數的行爲,而後繼續。想象一下讓這個過程自動化,聽起來不錯,不是嗎?

管理斷點

Xcode 提供了一系列工具來建立和管理斷點。咱們會一個個看過來並介紹 LLDB 中等價的命令 (是的,你能夠在調試器內部添加斷點)。

在 Xcode 的左側面板,有一組按鈕。其中一個看起來像斷點。點擊它打開斷點導航,這是一個能夠快速管理全部斷點的面板。

在這裏你能夠看到全部的斷點 - 在 LLDB 中經過 breakpoint list (或者 br li) 命令也作一樣的事兒。你也能夠點擊單個斷點來開啓或關閉 - 在 LLDB 中使用 breakpoint enable <breakpointID> 和 breakpoint disable <breakpointID>

(lldb) br li
Current breakpoints: 1: file = '/Users/arig/Desktop/DebuggerDance/DebuggerDance/main.m', line = 16, locations = 1, resolved = 1, hit count = 1 1.1: where = DebuggerDance`main + 27 at main.m:16, address = 0x000000010a3f6cab, resolved, hit count = 1 (lldb) br dis 1 1 breakpoints disabled. (lldb) br li Current breakpoints: 1: file = '/Users/arig/Desktop/DebuggerDance/DebuggerDance/main.m', line = 16, locations = 1 Options: disabled 1.1: where = DebuggerDance`main + 27 at main.m:16, address = 0x000000010a3f6cab, unresolved, hit count = 1 (lldb) br del 1 1 breakpoints deleted; 0 breakpoint locations disabled. (lldb) br li No breakpoints currently set. 

建立斷點

在上面的例子中,咱們經過在源碼頁面器的滾槽 16 上點擊來建立斷點。你能夠經過把斷點拖拽出滾槽,而後釋放鼠標來刪除斷點 (消失時會有一個很是可愛的噗的一下的動畫)。你也能夠在斷點導航頁選擇斷點,而後按下刪除鍵刪除。

要在調試器中建立斷點,可使用 breakpoint set 命令。

(lldb) breakpoint set -f main.m -l 16 Breakpoint 1: where = DebuggerDance`main + 27 at main.m:16, address = 0x000000010a3f6cab 

也可使用縮寫形式 br。雖然 b 是一個徹底不一樣的命令 (_regexp-break 的縮寫),但剛好也能夠實現和上面一樣的效果。

(lldb) b main.m:17 Breakpoint 2: where = DebuggerDance`main + 52 at main.m:17, address = 0x000000010a3f6cc4 

也能夠在一個符號 (C 語言函數) 上建立斷點,而徹底不用指定哪一行 

(lldb) b isEven
Breakpoint 3: where = DebuggerDance`isEven + 16 at main.m:4, address = 0x000000010a3f6d00 (lldb) br s -F isEven Breakpoint 4: where = DebuggerDance`isEven + 16 at main.m:4, address = 0x000000010a3f6d00 

這些斷點會準確的中止在函數的開始。Objective-C 的方法也徹底能夠:

(lldb) breakpoint set -F "-[NSArray objectAtIndex:]" Breakpoint 5: where = CoreFoundation`-[NSArray objectAtIndex:], address = 0x000000010ac7a950 (lldb) b -[NSArray objectAtIndex:] Breakpoint 6: where = CoreFoundation`-[NSArray objectAtIndex:], address = 0x000000010ac7a950 (lldb) breakpoint set -F "+[NSSet setWithObject:]" Breakpoint 7: where = CoreFoundation`+[NSSet setWithObject:], address = 0x000000010abd3820 (lldb) b +[NSSet setWithObject:] Breakpoint 8: where = CoreFoundation`+[NSSet setWithObject:], address = 0x000000010abd3820 

若是想在 Xcode 的UI上建立符號斷點,你能夠點擊斷點欄左側的 + 按鈕。

而後選擇第三個選項:

這時會出現一個彈出框,你能夠在裏面添加例如 -[NSArray objectAtIndex:] 這樣的符號斷點。這樣每次調用這個函數的時候,程序都會中止,無論是你調用仍是蘋果調用。

若是你 Xcode 的 UI 上右擊任意斷點,而後選擇 "Edit Breakpoint" 的話,會有一些很是誘人的選擇。

這裏,斷點已經被修改成只有當 i 是 99 的時候纔會中止。你也可使用 "ignore" 選項來告訴斷點最初的 n 次調用 (而且條件爲真的時候) 的時候不要中止。

接下來介紹 'Add Action' 按鈕...

斷點行爲 (Action)

上面的例子中,你或許想知道每一次到達斷點的時候 i 的值。咱們可使用 p i 做爲斷點行爲。這樣每次到達斷點的時候,都會自動運行這個命令。

你也能夠添加多個行爲,能夠是調試器命令,shell 命令,也能夠是更直接的打印:

能夠看到它打印 i,而後大聲念出那個句子,接着打印了自定義的表達式。

下面是在 LLDB 而不是 Xcode 的 UI 中作這些的時候,看起來的樣子。

(lldb) breakpoint set -F isEven
Breakpoint 1: where = DebuggerDance`isEven + 16 at main.m:4, address = 0x00000001083b5d00 (lldb) breakpoint modify -c 'i == 99' 1 (lldb) breakpoint command add 1 Enter your debugger command(s). Type 'DONE' to end. > p i > DONE (lldb) br li 1 1: name = 'isEven', locations = 1, resolved = 1, hit count = 0 Breakpoint commands: p i Condition: i == 99 1.1: where = DebuggerDance`isEven + 16 at main.m:4, address = 0x00000001083b5d00, resolved, hit count = 0 

接下來講說自動化。

賦值後繼續運行

看編輯斷點彈出窗口的底部,你還會看到一個選項: "Automatically continue after evaluation actions." 。它僅僅是一個選擇框,可是卻很強大。選中它,調試器會運行你全部的命令,而後繼續運行。看起來就像沒有執行任何斷點同樣 (除非斷點太多,運行須要一段時間,拖慢了你的程序)。

這個選項框的效果和讓最後斷點的最後一個行爲是 continue 同樣。選框只是讓這個操做變得更簡單。調試器的輸出是:

(lldb) breakpoint set -F isEven
Breakpoint 1: where = DebuggerDance`isEven + 16 at main.m:4, address = 0x00000001083b5d00 (lldb) breakpoint command add 1 Enter your debugger command(s). Type 'DONE' to end. > continue > DONE (lldb) br li 1 1: name = 'isEven', locations = 1, resolved = 1, hit count = 0 Breakpoint commands: continue 1.1: where = DebuggerDance`isEven + 16 at main.m:4, address = 0x00000001083b5d00, resolved, hit count = 0 

執行斷點後自動繼續運行,容許你徹底經過斷點來修改程序!你能夠在某一行中止,運行一個 expression 命令來改變變量,而後繼續運行。

例子

想一想所謂的"打印調試"技術吧,不要這麼作:

NSLog(@"%@", whatIsInsideThisThing); 

而是用個打印變量的斷點替換 log 語句,而後繼續運行。

也不要:

int calculateTheTrickyValue { return 9; /* Figure this out later. ... } 

而是加一個使用 thread return 9 命令的斷點,而後讓它繼續運行。

符號斷點加上 action 真的很強大。你也能夠在你朋友的 Xcode 工程上添加一些斷點,而且加上大聲朗讀某些東西的 action。看看他們要花多久才能弄明白髮生了什麼。😄

徹底在調試器內運行

在開始舞蹈以前,還有一件事要看一看。實際上你能夠在調試器中執行任何 C/Objective-C/C++/Swift 的命令。惟一的缺點就是不能建立新函數... 這意味着不能建立新的類,block,函數,有虛擬函數的 C++ 類等等。除此以外,它均可以作。

咱們能夠申請分配一些字節:

(lldb) e char *$str = (char *)malloc(8) (lldb) e (void)strcpy($str, "munkeys") (lldb) e $str[1] = 'o' (char) $0 = 'o' (lldb) p $str (char *) $str = 0x00007fd04a900040 "monkeys" 

咱們能夠查看內存 (使用 x 命令),來看看新數組中的四個字節:

(lldb) x/4c $str 0x7fd04a900040: monk 

咱們也能夠去掉 3 個字節 (x 命令須要斜引號,由於它只有一個內存地址的參數,而不是表達式;使用 help x 來得到更多信息):

(lldb) x/1w `$str + 3` 0x7fd04a900043: keys 

作完了以後,必定不要忘了釋放內存,這樣纔不會內存泄露。(哈,雖然這是調試器用到的內存):

(lldb) e (void)free($str) 

讓咱們起舞

如今咱們已經知道基本的步調了,是時候開始跳舞並玩一些瘋狂的事情了。我曾經寫過一篇 NSArray 深度探究的博客。這篇博客用了不少 NSLog 語句,但實際上個人全部探索都是在調試器中完成的。看看你能不能弄明白怎麼作的,這會是一個有意思的練習。

不用斷點調試

程序運行時,Xcode 的調試條上會出現暫停按鈕,而不是繼續按鈕:

點擊按鈕會暫停 app (這會運行 process interrupt 命令,由於 LLDB 老是在背後運行)。這會讓你能夠訪問調試器,但看起來能夠作的事情很少,由於在當前做用域沒有變量,也沒有特定的代碼讓你看。

這就是有意思的地方。若是你正在運行 iOS app,你能夠試試這個: (由於全局變量是可訪問的)

(lldb) po [[[UIApplication sharedApplication] keyWindow] recursiveDescription] <UIWindow: 0x7f82b1fa8140; frame = (0 0; 320 568); gestureRecognizers = <NSArray: 0x7f82b1fa92d0>; layer = <UIWindowLayer: 0x7f82b1fa8400>> | <UIView: 0x7f82b1d01fd0; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x7f82b1e2e0a0>> 

你能夠看到整個層次。Chisel 中 pviews 就是這麼實現的。

更新UI

有了上面的輸出,咱們能夠獲取這個 view:

(lldb) e id $myView = (id)0x7f82b1d01fd0 

而後在調試器中改變它的背景色:

(lldb) e (void)[$myView setBackgroundColor:[UIColor blueColor]] 

可是隻有程序繼續運行以後纔會看到界面的變化。由於改變的內容必須被髮送到渲染服務中,而後顯示纔會被更新。

渲染服務其實是一個另外的進程 (被稱做 backboardd)。這就是說即便咱們正在調試的內容所在的進程被打斷了,backboardd也仍是繼續運行着的。

這意味着你能夠運行下面的命令,而不用繼續運行程序:

(lldb) e (void)[CATransaction flush] 

即便你仍然在調試器中,UI 也會在模擬器或者真機上實時更新。Chisel 爲此提供了一個別名叫作 caflush,這個命令被用來實現其餘的快捷命令,例如 hide <view>show <view> 以及其餘不少命令。全部 Chisel 的命令都有文檔,因此安裝後隨意運行 help show 來看更多信息。

Push 一個 View Controller

想象一個以 UINavigationController 爲 root ViewController 的應用。你能夠經過下面的命令,輕鬆地獲取它:

(lldb) e id $nvc = [[[UIApplication sharedApplication] keyWindow] rootViewController] 

而後 push 一個 child view controller:

(lldb) e id $vc = [UIViewController new] (lldb) e (void)[[$vc view] setBackgroundColor:[UIColor yellowColor]] (lldb) e (void)[$vc setTitle:@"Yay!"] (lldb) e (void)[$nvc pushViewContoller:$vc animated:YES] 

最後運行下面的命令:

(lldb) caflush // e (void)[CATransaction flush] 

navigation Controller 就會馬上就被 push 到你眼前。

查找按鈕的 target

想象你在調試器中有一個 $myButton 的變量,能夠是建立出來的,也能夠是從 UI 上抓取出來的,或者是你中止在斷點時的一個局部變量。你想知道,按鈕按下的時候誰會接收到按鈕發出的 action。很是簡單:

(lldb) po [$myButton allTargets] {( <MagicEventListener: 0x7fb58bd2e240> )} (lldb) po [$myButton actionsForTarget:(id)0x7fb58bd2e240 forControlEvent:0] <__NSArrayM 0x7fb58bd2aa40>( _handleTap: ) 

如今你或許想在它發生的時候加一個斷點。在 -[MagicEventListener _handleTap:] 設置一個符號斷點就能夠了,在 Xcode 和 LLDB 中均可以,而後你就能夠點擊按鈕並停在你所但願的地方了。

觀察實例變量的變化

假設你有一個 UIView,不知道爲何它的 _layer 實例變量被重寫了 (糟糕)。由於有可能並不涉及到方法,咱們不能使用符號斷點。相反的,咱們想監視何時這個地址被寫入。

首先,咱們須要找到 _layer 這個變量在對象上的相對位置:

(lldb) p (ptrdiff_t)ivar_getOffset((struct Ivar *)class_getInstanceVariable([MyView class], "_layer")) (ptrdiff_t) $0 = 8 

如今咱們知道 ($myView + 8) 是被寫入的內存地址:

(lldb) watchpoint set expression -- (int *)$myView + 8 Watchpoint created: Watchpoint 3: addr = 0x7fa554231340 size = 8 state = enabled type = w new value: 0x0000000000000000 

這被以 wivar $myView _layer 加入到 Chisel 中。

非重寫方法的符號斷點

假設你想知道 -[MyViewController viewDidAppear:] 何時被調用。若是這個方法並無在MyViewController 中實現,而是在其父類中實現的,該怎麼辦呢?試着設置一個斷點,會出現如下結果:

(lldb) b -[MyViewController viewDidAppear:]
Breakpoint 1: no locations (pending). WARNING: Unable to resolve breakpoint to any actual locations. 

由於 LLDB 會查找一個符號,可是實際在這個類上卻找不到,因此斷點也永遠不會觸發。你須要作的是爲斷點設置一個條件 [self isKindOfClass:[MyViewController class]],而後把斷點放在 UIViewController 上。正常狀況下這樣設置一個條件能夠正常工做。可是這裏不會,由於咱們沒有父類的實現。

viewDidAppear: 是蘋果實現的方法,所以沒有它的符號;在方法內沒有 self 。若是想在符號斷點上使用 self,你必須知道它在哪裏 (它可能在寄存器上,也可能在棧上;在 x86 上,你能夠在 $esp+4 找到它)。可是這是很痛苦的,由於如今你必須至少知道四種體系結構 (x86,x86-64,armv7,armv64)。想象你須要花多少時間去學習命令集以及它們每個的調用約定,而後正確的寫一個在你的超類上設置斷點而且條件正確的命令。幸運的是,這個在 Chisel 被解決了。這被成爲 bmessage

(lldb) bmessage -[MyViewController viewDidAppear:]
Setting a breakpoint at -[UIViewController viewDidAppear:] with condition (void*)object_getClass((id)$rdi) == 0x000000010e2f4d28 Breakpoint 1: where = UIKit`-[UIViewController viewDidAppear:], address = 0x000000010e11533c 

LLDB 和 Python

LLDB 有內建的,完整的 Python 支持。在LLDB中輸入 script,會打開一個 Python REPL。你也能夠輸入一行 python 語句做爲 script 命令 的參數,這能夠運行 python 語句而不進入REPL:

(lldb) script import os (lldb) script os.system("open http://www.objc.io/") 

這樣就容許你創造各類酷的命令。把下面的語句放到文件 ~/myCommands.py 中:

def caflushCommand(debugger, command, result, internal_dict): debugger.HandleCommand("e (void)[CATransaction flush]") 

而後再 LLDB 中運行:

command script import ~/myCommands.py 

或者把這行命令放在 /.lldbinit 裏,這樣每次進入 LLDB 時都會自動運行。Chisel 其實就是一個 Python 腳本的集合,這些腳本拼接 (命令) 字符串 ,而後讓 LLDB 執行。很簡單,不是嗎?

緊握調試器這一武器

LLDB 能夠作的事情不少。大多數人習慣於使用 ppons 和 c,但實際上除此以外,LLDB 能夠作的還有不少。掌握全部的命令 (實際上並非不少),會讓你在揭示代碼運行時的運行狀態,尋找 bug,強制執行特定的運行路徑時得到更大的能力。你甚至能夠構建簡單的交互原型 - 好比要是如今以 modal 方式彈出一個 View Controller 會怎麼樣?使用調試器,一試便知。

打開 LLDB,輸入 help,看一看列舉的命令。你嘗試過多少?用了多少?

希望 NSLog 看起來再也不那麼吸引你

相關文章
相關標籤/搜索