[譯] Xcode 和 LLDB 高階除錯教程:第 3 部分

kirinzer發表於2019-07-24

在這三部分教程的第一部分和第二部分中,我們已經介紹瞭如何利用 Xcode 斷點來控制一個存在的屬性值,並且通過表示式語句注入新的程式碼行。我們還探索了觀察點這種特殊型別的斷點。

我開發了一個特意帶有幾個錯誤的演示專案,詳細說明了如何使用不同型別的斷點配合 LLDB 來修復專案/應用程式中的錯誤。

如果你還沒有看過本教程的 第一部分第二部分,最好先看過它們再繼續閱讀本文。

最後,本教程的指導原則是:

第一次執行應用程式後,你不必停止編譯器或重新執行應用程式,你將在執行時修復這些錯誤。

符號斷點 ?

到目前為止我們還要做什麼?

  1. 導航欄左側指示使用者載入次數的標籤沒有更新。

這裡有一些步驟可以復現這最後一個需要處理的錯誤:

✦ 滾動到表檢視的頂部,然後下拉重新整理。

✦ 滾動到表檢視的底部去載入更多文章。[7 次 ?]

✦ 在每次成功獲取到新的文章之後,左側標籤並沒有被更新。

需要指出的是整型屬性 pageNumber 回答了這個問題,使用者已經載入了文章多少次?(換句話說,導航欄左側的標籤應該被 pageNumber 屬性的值更新)。我們可以確信的是,在之前的修復中 pageNumber 屬性的值已經可以更新了。因此現在的問題在於沒有將它的值設定給導航欄左側的標籤。

在這種情況下,符號斷點會介入。想象一下,符號斷點就像偵錯程式在玩尋寶,而你會提供一些尋寶的線索。對你來說,這會發生在更新導航欄左側標籤的程式碼片段中。

讓我告訴你接下來怎麼做。

展開 Breakpoint navigator,接著點選左下角 + 按鈕,選擇 Symbolic Breakpoint。

[譯] Xcode 和 LLDB 高階除錯教程:第 3 部分

在 Symbol 欄新增如下符號

[UILabel setText:]
複製程式碼

[譯] Xcode 和 LLDB 高階除錯教程:第 3 部分

不要 勾選 “Automatically continue after evaluating actions” 選項框。

我們所做的只是告訴偵錯程式,當任何一個 UILabel 的 setText 方法被呼叫的時候,它就會暫停。注意這裡在建立了一個符號斷點之後,一個子斷點會被新增。

[譯] Xcode 和 LLDB 高階除錯教程:第 3 部分

這是來自偵錯程式的反饋,它能夠解析這個建立的符號斷點到 UIKitCore 框架的特定位置。在其他情況下,偵錯程式也許會解析到多個位置。

現在一切就緒,下拉以重新整理表檢視的文章。當你釋放之後,偵錯程式就會暫停,接著你會看到如下圖的東西:

[譯] Xcode 和 LLDB 高階除錯教程:第 3 部分

在這時你會看到一些 UIKitCore 框架的彙編程式碼,在左側的是導致偵錯程式暫停的堆疊資訊。下一步我們要做的是,檢查在偵錯程式暫停的位置傳入 Objective-C 訊息的引數。在 lldb 控制檯輸入下面的命令:

po $arg1
複製程式碼

[譯] Xcode 和 LLDB 高階除錯教程:第 3 部分

這會指出持有第一個引數的暫存器。我們能清楚的看到接受這個 Objective-C 訊息的是一個 UILabel 例項。這個 UILabel 例項有一個文字值指向一個文章的標籤。這不是我們所感興趣的,不過讓我們繼續暫存器檢查。

在 lldb 控制檯,輸入如下指令:

po $arg2
複製程式碼

[譯] Xcode 和 LLDB 高階除錯教程:第 3 部分

$arg2 始終指向 Objective-C 訊息的選擇器。在某些情況下,lldb 並不完全的清楚引數的型別,因此我們需要做一些型別轉換的工作。

在 lldb 控制檯,輸入如下指令:

po (SEL)$arg2
複製程式碼

[譯] Xcode 和 LLDB 高階除錯教程:第 3 部分

現在我們很清楚的看到了當前 Objective-C 訊息的選擇器。

在 lldb 控制檯,輸入如下指令:

po $arg3
複製程式碼

[譯] Xcode 和 LLDB 高階除錯教程:第 3 部分

$arg3 始終指向傳入方法的第一個引數。在我們的情形下,傳入 setText 方法的引數一個字串。

繼續執行程式。偵錯程式會再次暫停。重複前面的步驟,最終,你發現這個 Objective-C 訊息屬於在表檢視裡的另一個文章標籤。直到我們找到我們感興趣的那個 UILabel 例項前,一遍又一遍的做這個事情確實很無趣。肯定有更好的方式。

你能夠做的一件事就是為符號斷點設定條件,以便在成功或滿足條件時暫停偵錯程式。它能夠檢查布林值或者等待條件達成諸如此類。

[譯] Xcode 和 LLDB 高階除錯教程:第 3 部分

然而,我們採用一種不同的方法。

One Shot!

將我們建立的符號斷點設定為不可用。

講道理,導航欄左側的標籤指示了使用者載入文章的次數,它會在 HTTP GET 請求成功完成之後被更新。找到有 pragma mark Networking 的部分。在 loadPosts 成功完成的回撥裡放置一個斷點。這個斷點應該放在如下的位置:

Objective-C

[self.tableView reloadData];
複製程式碼

[譯] Xcode 和 LLDB 高階除錯教程:第 3 部分

Swift

self.tableView.reloadData()
複製程式碼

[譯] Xcode 和 LLDB 高階除錯教程:第 3 部分

這會確保符號斷點只有在表檢視重新載入資料之後才會被觸發,所有相等的標籤都已經被更新。

不要 勾選 “Automatically continue after evaluating actions” 選項框。新增如下的偵錯程式命令動作:

breakpoint set --one-shot true -name '-[UILabel setText:]'
複製程式碼

???

讓我們拆解這個命令:

  1. breakpoint set --one-shot true 會建立一個 “one-short” 斷點。one-shot 斷點是一種建立之後,首次觸發就會自動刪除的斷點。

  2. -name ‘- [UILabel setText:]’ 給建立的 one-shot 斷點設定了一個符號名。這和你上一節所做的非常相似。

讓我總結一下這一部分。你所做的有:

  1. 在發起 GET 請求成功完成的回撥裡新增斷點(A)。

  2. 新增偵錯程式命令動作去 建立 符號斷點(B)和上一節建立的很相似。這個符號是 UILabel setText 方法。

  3. 將你建立的符號斷點(B)設定為一個 one-shot 斷點。one-shot 斷點在觸發後會被自動刪除,這意味著符號斷點只會暫停偵錯程式一次。

  4. 斷點(A)被放置在表檢視載入完成之後,因此建立的符號斷點(B)不會因任何和表檢視相關聯的標籤而暫停偵錯程式。

現在下拉表檢視去重新整理。我們會得到如下內容:

Objective-C

Swift

由於設定了 one-shot 斷點偵錯程式停在了斷點(A)的位置。

繼續執行程式。

你會返回到 UIKitCore 框架的彙編程式碼。

[譯] Xcode 和 LLDB 高階除錯教程:第 3 部分

讓我們檢查一下符號斷點引數的 Objective-C 訊息。

在 lldb 控制檯,輸入如下的指令:

po $arg1
複製程式碼

[譯] Xcode 和 LLDB 高階除錯教程:第 3 部分

哇哦,看起來你找到了寶藏! ???

是時候把我們的目光轉移到堆疊跟蹤資訊了。走到點 1 的位置。

Objective-C

Swift

它會引導你到這塊更新 pageNumberLabel 文字的程式碼。這塊程式碼很明顯為文字始終設定了整型值為 0 而不是 pageNumber 屬性的格式字串。讓我們在實際修改程式碼前先測試一下。

你現在已經是行家了 ?

在已標記的程式碼分隔線下新增一個斷點。新增如下的偵錯程式命令動作:

Objective-C

expression self.pageNumberLabel.text = [NSString stringWithFormat:@"Page %tu", self.pageNumber]
複製程式碼

[譯] Xcode 和 LLDB 高階除錯教程:第 3 部分

Swift

expression pageNumberLabel.text = String(format: "Page %tu", pageNumber)
複製程式碼

[譯] Xcode 和 LLDB 高階除錯教程:第 3 部分

移除或者停用斷點(A),相應地,斷點(B)也會被停用。

現在下拉重新整理和載入更多文章。左側導航欄標籤將會被更新。 ?

任務完成! ? ?

現在你可以停止編譯器並且在程式碼中去修復我們討論的這些問題。

總結

在這個教程裡,你學會了

  1. 如何使用斷點配合偵錯程式動作表示式去控制存在的屬性值。

  2. 如何使用斷點配合偵錯程式動作表示式注入程式碼。

  3. 如何為某個屬性設定觀察點監視屬性值的變化。

  4. 如何基於定義的符號使用符號斷點暫停偵錯程式。

  5. 如何使用 one-shot 斷點。

  6. 如何使用 one-shot 斷點配合符號斷點。

除錯愉快! ?

第三方工具

為了本教程的方便,我使用了下面的第三方工具。

如果發現譯文存在錯誤或其他需要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可獲得相應獎勵積分。文章開頭的 本文永久連結 即為本文在 GitHub 上的 MarkDown 連結。


掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章