- 原文地址:Xcode and LLDB Advanced Debugging Tutorial: Part 3
- 原文作者:Fady Derias
- 譯文出自:掘金翻譯計劃
- 本文永久連結:github.com/xitu/gold-m…
- 譯者:kirinzer
- 校對者:swants, iWeslie
在這三部分教程的第一部分和第二部分中,我們已經介紹瞭如何利用 Xcode 斷點來控制一個存在的屬性值,並且通過表示式語句注入新的程式碼行。我們還探索了觀察點這種特殊型別的斷點。
我開發了一個特意帶有幾個錯誤的演示專案,詳細說明了如何使用不同型別的斷點配合 LLDB 來修復專案/應用程式中的錯誤。
如果你還沒有看過本教程的 第一部分 和 第二部分,最好先看過它們再繼續閱讀本文。
最後,本教程的指導原則是:
第一次執行應用程式後,你不必停止編譯器或重新執行應用程式,你將在執行時修復這些錯誤。
符號斷點 ?
到目前為止我們還要做什麼?
- 導航欄左側指示使用者載入次數的標籤沒有更新。
這裡有一些步驟可以復現這最後一個需要處理的錯誤:
✦ 滾動到表檢視的頂部,然後下拉重新整理。
✦ 滾動到表檢視的底部去載入更多文章。[7 次 ?]
✦ 在每次成功獲取到新的文章之後,左側標籤並沒有被更新。
需要指出的是整型屬性 pageNumber
回答了這個問題,使用者已經載入了文章多少次?(換句話說,導航欄左側的標籤應該被 pageNumber
屬性的值更新)。我們可以確信的是,在之前的修復中 pageNumber
屬性的值已經可以更新了。因此現在的問題在於沒有將它的值設定給導航欄左側的標籤。
在這種情況下,符號斷點會介入。想象一下,符號斷點就像偵錯程式在玩尋寶,而你會提供一些尋寶的線索。對你來說,這會發生在更新導航欄左側標籤的程式碼片段中。
讓我告訴你接下來怎麼做。
展開 Breakpoint navigator,接著點選左下角 + 按鈕,選擇 Symbolic Breakpoint。
在 Symbol 欄新增如下符號
[UILabel setText:]
複製程式碼
不要 勾選 “Automatically continue after evaluating actions” 選項框。
我們所做的只是告訴偵錯程式,當任何一個 UILabel 的 setText 方法被呼叫的時候,它就會暫停。注意這裡在建立了一個符號斷點之後,一個子斷點會被新增。
這是來自偵錯程式的反饋,它能夠解析這個建立的符號斷點到 UIKitCore
框架的特定位置。在其他情況下,偵錯程式也許會解析到多個位置。
現在一切就緒,下拉以重新整理表檢視的文章。當你釋放之後,偵錯程式就會暫停,接著你會看到如下圖的東西:
在這時你會看到一些 UIKitCore 框架的彙編程式碼,在左側的是導致偵錯程式暫停的堆疊資訊。下一步我們要做的是,檢查在偵錯程式暫停的位置傳入 Objective-C 訊息的引數。在 lldb 控制檯輸入下面的命令:
po $arg1
複製程式碼
這會指出持有第一個引數的暫存器。我們能清楚的看到接受這個 Objective-C 訊息的是一個 UILabel 例項。這個 UILabel 例項有一個文字值指向一個文章的標籤。這不是我們所感興趣的,不過讓我們繼續暫存器檢查。
在 lldb 控制檯,輸入如下指令:
po $arg2
複製程式碼
$arg2 始終指向 Objective-C 訊息的選擇器。在某些情況下,lldb 並不完全的清楚引數的型別,因此我們需要做一些型別轉換的工作。
在 lldb 控制檯,輸入如下指令:
po (SEL)$arg2
複製程式碼
現在我們很清楚的看到了當前 Objective-C 訊息的選擇器。
在 lldb 控制檯,輸入如下指令:
po $arg3
複製程式碼
$arg3 始終指向傳入方法的第一個引數。在我們的情形下,傳入 setText 方法的引數一個字串。
繼續執行程式。偵錯程式會再次暫停。重複前面的步驟,最終,你發現這個 Objective-C 訊息屬於在表檢視裡的另一個文章標籤。直到我們找到我們感興趣的那個 UILabel 例項前,一遍又一遍的做這個事情確實很無趣。肯定有更好的方式。
你能夠做的一件事就是為符號斷點設定條件,以便在成功或滿足條件時暫停偵錯程式。它能夠檢查布林值或者等待條件達成諸如此類。
然而,我們採用一種不同的方法。
One Shot!
將我們建立的符號斷點設定為不可用。
講道理,導航欄左側的標籤指示了使用者載入文章的次數,它會在 HTTP GET 請求成功完成之後被更新。找到有 pragma mark Networking
的部分。在 loadPosts
成功完成的回撥裡放置一個斷點。這個斷點應該放在如下的位置:
Objective-C
[self.tableView reloadData];
複製程式碼
Swift
self.tableView.reloadData()
複製程式碼
這會確保符號斷點只有在表檢視重新載入資料之後才會被觸發,所有相等的標籤都已經被更新。
不要 勾選 “Automatically continue after evaluating actions” 選項框。新增如下的偵錯程式命令動作:
breakpoint set --one-shot true -name '-[UILabel setText:]'
複製程式碼
???
讓我們拆解這個命令:
-
breakpoint set --one-shot true 會建立一個 “one-short” 斷點。one-shot 斷點是一種建立之後,首次觸發就會自動刪除的斷點。
-
-name ‘- [UILabel setText:]’
給建立的 one-shot 斷點設定了一個符號名。這和你上一節所做的非常相似。
讓我總結一下這一部分。你所做的有:
-
在發起 GET 請求成功完成的回撥裡新增斷點(A)。
-
新增偵錯程式命令動作去 建立 符號斷點(B)和上一節建立的很相似。這個符號是
UILabel
setText
方法。 -
將你建立的符號斷點(B)設定為一個 one-shot 斷點。one-shot 斷點在觸發後會被自動刪除,這意味著符號斷點只會暫停偵錯程式一次。
-
斷點(A)被放置在表檢視載入完成之後,因此建立的符號斷點(B)不會因任何和表檢視相關聯的標籤而暫停偵錯程式。
現在下拉表檢視去重新整理。我們會得到如下內容:
由於設定了 one-shot 斷點偵錯程式停在了斷點(A)的位置。
繼續執行程式。
你會返回到 UIKitCore 框架的彙編程式碼。
讓我們檢查一下符號斷點引數的 Objective-C 訊息。
在 lldb 控制檯,輸入如下的指令:
po $arg1
複製程式碼
哇哦,看起來你找到了寶藏! ???
是時候把我們的目光轉移到堆疊跟蹤資訊了。走到點 1 的位置。
它會引導你到這塊更新 pageNumberLabel
文字的程式碼。這塊程式碼很明顯為文字始終設定了整型值為 0
而不是 pageNumber
屬性的格式字串。讓我們在實際修改程式碼前先測試一下。
你現在已經是行家了 ?
在已標記的程式碼分隔線下新增一個斷點。新增如下的偵錯程式命令動作:
Objective-C
expression self.pageNumberLabel.text = [NSString stringWithFormat:@"Page %tu", self.pageNumber]
複製程式碼
Swift
expression pageNumberLabel.text = String(format: "Page %tu", pageNumber)
複製程式碼
移除或者停用斷點(A),相應地,斷點(B)也會被停用。
現在下拉重新整理和載入更多文章。左側導航欄標籤將會被更新。 ?
任務完成! ? ?
現在你可以停止編譯器並且在程式碼中去修復我們討論的這些問題。
總結
在這個教程裡,你學會了
-
如何使用斷點配合偵錯程式動作表示式去控制存在的屬性值。
-
如何使用斷點配合偵錯程式動作表示式注入程式碼。
-
如何為某個屬性設定觀察點監視屬性值的變化。
-
如何基於定義的符號使用符號斷點暫停偵錯程式。
-
如何使用 one-shot 斷點。
-
如何使用 one-shot 斷點配合符號斷點。
除錯愉快! ?
第三方工具
為了本教程的方便,我使用了下面的第三方工具。
如果發現譯文存在錯誤或其他需要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可獲得相應獎勵積分。文章開頭的 本文永久連結 即為本文在 GitHub 上的 MarkDown 連結。
掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。