[譯] Xcode 和 LLDB 高級調試教程:第 3 部分

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

我開發了一個特地帶有幾個錯誤的演示項目,詳細說明了如何使用不一樣類型的斷點配合 LLDB 來修復項目/應用程序中的錯誤。android

若是你尚未看過本教程的 第一部分第二部分,最好先看過它們再繼續閱讀本文。ios

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

第一次運行應用程序後,你沒必要中止編譯器或從新運行應用程序,你將在運行時修復這些錯誤。github

符號斷點 🔶

到目前爲止咱們還要作什麼?express

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

這裏有一些步驟能夠復現這最後一個須要處理的錯誤:json

✦ 滾動到表視圖的頂部,而後下拉刷新。後端

✦ 滾動到表視圖的底部去加載更多文章。[7 次 😉]xcode

✦ 在每次成功獲取到新的文章以後,左側標籤並無被更新。bash

須要指出的是整型屬性 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:]'
複製代碼

🤨🧐🤔

讓咱們拆解這個命令:

  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 框架的彙編代碼。

讓咱們檢查一下符號斷點參數的 Objective-C 消息。

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

po $arg1
複製代碼

哇哦,看起來你找到了寶藏! 🥇🏆🎉

是時候把咱們的目光轉移到堆棧跟蹤信息了。走到點 1 的位置。

Objective-C

Swift

它會引導你到這塊更新 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)也會被停用。

如今下拉刷新和加載更多文章。左側導航欄標籤將會被更新。 🎉

任務完成! 💪 💪

如今你能夠中止編譯器而且在代碼中去修復咱們討論的這些問題。

總結

在這個教程裏,你學會了

  1. 如何使用斷點配合調試器動做表達式去控制存在的屬性值。

  2. 如何使用斷點配合調試器動做表達式注入代碼。

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

  4. 如何基於定義的符號使用符號斷點暫停調試器。

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

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

調試愉快! 😊

第三方工具

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

若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。


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

相關文章
相關標籤/搜索