開(kāi)始學(xué)習(xí)LLDB命令(第四章:停在代碼中)

不管你使用的是Swift, Objective-C, C++, C,或者其他的編程語(yǔ)言, 你都需要學(xué)習(xí)如何創(chuàng)建一個(gè)斷點(diǎn).在Xcode這樣的GUI程序中, 在編輯界面的左邊點(diǎn)一下創(chuàng)建一個(gè)斷點(diǎn)是非常簡(jiǎn)單的, 但是在LLDB控制臺(tái)中可以讓你更靈活的控制斷點(diǎn).
在本章中, 你將會(huì)學(xué)到LLDB中所有關(guān)于斷點(diǎn)的知識(shí).

Signals

在本章中, 你將會(huì)使用我提供的一個(gè)工程; 這個(gè)工程的名字叫做Signals, 你可以在資源文件夾里面找到它.

page42image6392.png

用Xcode打開(kāi)Signals項(xiàng)目.Signals是一個(gè)以美式橄欖球項(xiàng)目為主題的APP, 顯示一些叫做nerdily的攻擊型打法.
在程序內(nèi)部, 這個(gè)項(xiàng)目會(huì)監(jiān)測(cè)幾個(gè)Unix signals并在Signals程序收到這些信號(hào)的時(shí)候顯示這些信號(hào).

Unix signals是進(jìn)程之間通信的基本形式. 例如, signals中的SIGSTOP可以保存一個(gè)進(jìn)程的狀態(tài)并且暫停進(jìn)程的執(zhí)行.與之對(duì)應(yīng)的是SIGCONT, 他告訴程序繼續(xù)執(zhí)行.這兩個(gè)signals都可以被調(diào)試器用來(lái)暫停或繼續(xù)程序的執(zhí)行.

在前面這些章節(jié)中這算是一個(gè)有趣的程序, 因?yàn)樗粌H用來(lái)瀏覽Unix 處理signal, 而且會(huì)高亮顯示(LLDB)控制的線(xiàn)程在處理經(jīng)過(guò)的Unix signals時(shí)發(fā)生了什么.默認(rèn)情況下, LLDB在處理不同的signals時(shí)有自定義的動(dòng)作.當(dāng)附加了LLDB以后有些signals是不會(huì)經(jīng)過(guò)被控制的線(xiàn)程的.

為了顯示signal, 你既可以在應(yīng)用程序中增加一個(gè)Signal, 也可以在外部的應(yīng)用程序中發(fā)送一個(gè)signal, 比如在Terminal中.

此外, 這里有一個(gè)UISwitch可以切換處理signal的代碼塊, 代碼塊的名字叫做sigprocmask來(lái)啟用或者禁用signal的處理.

最后, Signal應(yīng)用程序還有在應(yīng)用程序里增加SIGSTOP的Timeout按鈕, 從根本上凍結(jié)應(yīng)用程序.然而, 如果LLDB附加到了Signals應(yīng)用程序上 (并且在默認(rèn)情況下就是這樣做的, 當(dāng)你通過(guò)Xcode來(lái)構(gòu)建和運(yùn)行Signals的時(shí)候), 調(diào)用SIGSTOP可以讓你在Xcode里用LLDB檢查線(xiàn)程的至此NG狀態(tài).

確保選中的是iPhone 7 Simulator. 構(gòu)建并運(yùn)行這個(gè)APP.一旦項(xiàng)目運(yùn)行起來(lái)了以后, 在Xcode的控制臺(tái)里找到并暫停調(diào)試器.

page43image20376.png

繼續(xù)運(yùn)行Xcode并留意觀察模擬器.當(dāng)調(diào)試器暫停然后繼續(xù)執(zhí)行的時(shí)候一行新數(shù)據(jù)會(huì)被添加到UITableView上. 這是Signals監(jiān)測(cè)器監(jiān)測(cè)到Unix signal的SIGSTOP事件時(shí)添加的一個(gè)數(shù)據(jù).當(dāng)線(xiàn)程被停止的時(shí)候, 任何新的signals都不會(huì)被立即處理, 因?yàn)榭梢哉J(rèn)為程序 已經(jīng)停止了.

Xcode斷點(diǎn)

在你學(xué)完之前, LLDB控制臺(tái)的斷點(diǎn)會(huì)是一個(gè)亮點(diǎn), 非常值得用來(lái)替代你再Xode中學(xué)到的斷點(diǎn);
Symbolic breakpoints 是一個(gè)Xcode中一個(gè)非常好的調(diào)試功能.它允許你在應(yīng)用程序中的某些symbol處設(shè)置斷點(diǎn).一個(gè)非常簡(jiǎn)單的例子就是-[NSObject init], 用的是NSObject實(shí)例的 init方法.
在Xcode中可以靈活的使用symbolic斷點(diǎn), 一旦你創(chuàng)建了一個(gè)斷點(diǎn), 下次你啟動(dòng)應(yīng)用程序的時(shí)候可以不用重新輸入.
現(xiàn)在你將會(huì)用symbolic斷點(diǎn)來(lái)顯示出所有被創(chuàng)建的NSObject的實(shí)例.
如果APP正在運(yùn)行那么就殺掉APP的進(jìn)程.然后切換到Breakpoint導(dǎo)航欄.在左下角點(diǎn)擊+按鈕, 并選擇 Symbolic Breakpoint選項(xiàng).

page44image11344.png

會(huì)出現(xiàn)一個(gè)彈窗.在彈窗的Symbol部分輸入:-[NSObject init].在Action下面, 選擇Add Action然后在下拉選項(xiàng)中選擇Debugger Command, 接下來(lái)在輸入框中輸入po [$arg1 class].
最后,選擇 Automatically continue after evaluating actions. 你的彈窗看起來(lái)應(yīng)該是下面的樣子:

page44image11776.png

構(gòu)建并運(yùn)行APP.當(dāng)Signals程序運(yùn)行的時(shí)候Xcode將會(huì)在控制臺(tái)輸出初始化的所有的類(lèi)名, 你會(huì)看到非常的多.
在這里你設(shè)置了一個(gè)-[NSObject init]每調(diào)用一次都會(huì)觸發(fā)的斷點(diǎn).當(dāng)斷點(diǎn)觸發(fā)的時(shí)候LLDB會(huì)運(yùn)行一條指令, 并自動(dòng)讓?xiě)?yīng)用程序繼續(xù)執(zhí)行.

注意: 在第十章“Assembly, Registers and Calling Convention”中你將會(huì)學(xué)到如果恰當(dāng)?shù)氖褂煤筒倏v寄存器, 但是現(xiàn)在只需要簡(jiǎn)單的知道`$arg1`與寄存器中的`$rdi`的同義詞, 可以簡(jiǎn)單的看作是調(diào)用`init`方法的類(lèi)的一個(gè)實(shí)例.

如果你完成了查看所有類(lèi)名的提取操作, 然后用在breakpoint導(dǎo)航欄中點(diǎn)擊右鍵選擇刪除斷點(diǎn)的方法刪除斷點(diǎn).
除了symbolic斷點(diǎn), Xcode還支持幾種其他錯(cuò)誤類(lèi)型的斷點(diǎn).其中有一個(gè)叫Exception Breakpoint.有時(shí)候你的程序會(huì)拋出一些錯(cuò)誤,這些錯(cuò)誤會(huì)導(dǎo)致崩潰.你的第一反應(yīng)應(yīng)該就是啟用exception breakpoint. Xcode將會(huì)自動(dòng)定位到出問(wèn)題的那行代碼, 這對(duì)于捕獲崩潰有很大的幫助.
最后, 還有一個(gè)斷點(diǎn)類(lèi)型是Swift Error Breakpoint.這種類(lèi)型的斷點(diǎn)本質(zhì)上是在在swift_willThrow方法中創(chuàng)建一個(gè)斷點(diǎn)并在swift拋出異常的時(shí)候觸發(fā).在我們使用那些容易出錯(cuò)的APIs的時(shí)候這是我們可以用的一個(gè)非常好的選項(xiàng).它可以讓你在沒(méi)有對(duì)代碼的對(duì)錯(cuò)做出判斷的時(shí)候快速的診斷出現(xiàn)的問(wèn)題.

LLDB斷點(diǎn)語(yǔ)法

現(xiàn)在你已經(jīng)掌握了Xcode中調(diào)試崩潰功能的課程, 是時(shí)候?qū)W習(xí)一下如何通過(guò)LLDB控制臺(tái)創(chuàng)建斷點(diǎn)了. 為了創(chuàng)建有用的斷點(diǎn), 你需要學(xué)習(xí)一下如何查詢(xún)你要找的東西.
image命令是一個(gè)查看內(nèi)部詳情的極其有用的工具, 對(duì)于設(shè)置斷點(diǎn)來(lái)說(shuō)是至關(guān)重要的.
本書(shū)中在搜索代碼的時(shí)候有兩個(gè)至關(guān)重要的配置.第一個(gè)就是:

(lldb) image lookup -n "-[UIViewController viewDidLoad]"

這個(gè)命令可以提取出-[UIViewController viewDidLoad]函數(shù)的加載地址.-n參數(shù)告訴LLDB可以查找symbol或者函數(shù)名.輸出的可能是下面這個(gè)樣子:

1 match found in /Applications/Xcode.app/Contents/Developer/Platforms/
iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk//System/
Library/Frameworks/UIKit.framework/UIKit:
        Address: UIKit[0x00000000001c67c8] (UIKit.__TEXT.__text +
1854120)
        Summary: UIKit`-[UIViewController viewDidLoad]

另外一個(gè)與之相似并非常有用的命令是:

(lldb) image lookup -rn test

這條命用來(lái)查找test這個(gè)單詞的而且是區(qū)分大小寫(xiě)的.如果小寫(xiě)的單詞test在當(dāng)前的可執(zhí)行文件中加載的任何模塊中的任何函數(shù)里(例如:UIKit, Foundation, Core Data, 等等) 找到了, 這條命令都會(huì)輸出出來(lái).

注意:當(dāng)你想要提取匹配到的內(nèi)容的參數(shù)時(shí)候使用`-n`選項(xiàng)(如果你查詢(xún)的內(nèi)容中包含空格則需要用引號(hào)引起來(lái)).`-n`只能幫助你提取出匹配到的斷點(diǎn)的準(zhǔn)確的參數(shù), 尤其是處理swift的時(shí)候, 同時(shí)`-rn`選項(xiàng)在本書(shū)中將會(huì)經(jīng)常用到, 因?yàn)槟愫芸炀蜁?huì)發(fā)現(xiàn)一個(gè)漂亮的正則表達(dá)式可以減少許多輸入.

Objective-C的屬性

學(xué)習(xí)如何在代碼中查詢(xún)加載的代碼是學(xué)習(xí)如何創(chuàng)建斷點(diǎn)的最終目標(biāo).
無(wú)論是Objective-C 還是Swift代碼在被編譯器創(chuàng)建的時(shí)候都有指定的屬性簽名, 我們需要使用不同的斷點(diǎn)策略.
例如在Signals項(xiàng)目中下面的Objective-C類(lèi)聲明了一個(gè)屬性:

@interface TestClass : NSObject
@property (nonatomic, strong) NSString *name;
@end

編譯器將會(huì)為這個(gè)屬性創(chuàng)建getter方法和setter方法.
getter看起來(lái)是這個(gè)樣子:

  -[TestClass name]

setter方法看起來(lái)是這個(gè)樣子:

  -[TestClass setName:]

構(gòu)建并運(yùn)行APP, 然后暫停調(diào)試器. 接下來(lái)在LLDB中鍵入以下命令驗(yàn)證一下這些方法是存在的:

(lldb) image lookup -n "-[TestClass name]"

在控制臺(tái)中你將會(huì)看到下面這些輸出:

1 match found in /Users/derekselander/Library/Developer/Xcode/
DerivedData/Signals-bqrjxlceauwfuihjesxmgfodimef/Build/Products/Debug-
iphonesimulator/Signals.app/Signals:
        Address: Signals[0x0000000100001470] (Signals.__TEXT.__text + 0)
        Summary: Signals`-[TestClass name] at TestClass.h:28

LLDB將會(huì)提取出可執(zhí)行文件中包含的函數(shù)的信息.這些代碼看起來(lái)有點(diǎn)嚇人, 但是卻有一些有用的信息在里面.
這些輸出告訴你LLDB在Signals可執(zhí)行文件中能夠找到這些函數(shù)的實(shí)現(xiàn).并且在__TEXT分段中準(zhǔn)確的偏移量是0x0000000100001470.LLDB還告訴我們這個(gè)方法生命在TestClass.h文件中的第28行.
你同樣可以用下面的方法查看setter方法:

(lldb) image lookup -n "-[TestClass setName:]"

你應(yīng)該會(huì)得到和之前類(lèi)似的輸出, 這一次顯示的是name這個(gè)屬性setter方法的實(shí)現(xiàn)和實(shí)現(xiàn)的地址.

Swift 屬性

在Swift中聲明屬性的語(yǔ)法有些不同. 看一下SwiftTestClass.swift文件你會(huì)發(fā)現(xiàn)下面的代碼:

class SwiftTestClass: NSObject {
  var name: String!
}

確保Signals項(xiàng)目正在運(yùn)行并且暫停在LLDB中. 你可以按下Command + K快速的清空LLDB的控制臺(tái).
在LLDB控制臺(tái)中輸入以下命令:

(lldb) image lookup -rn Signals.SwiftTestClass.name.setter

你將會(huì)得到一些類(lèi)似下面的輸出:

2 matches found in /Users/derekselander/Library/Developer/Xcode/
DerivedData/Signals-bqrjxlceauwfuihjesxmgfodimef/Build/Products/Debug-
iphonesimulator/Signals.app/Signals:
        Address: Signals[0x000000010000aba0] (Signals.__TEXT.__text +
38704)
        Summary: Signals`@objc Signals.SwiftTestClass.name.setter :
Swift.ImplicitlyUnwrappedOptional<Swift.String> at SwiftTestClass.swift
Address: Signals[0x000000010000ac60] (Signals.__TEXT.__text + 38896)
        Summary: Signals`Signals.SwiftTestClass.name.setter :
Swift.ImplicitlyUnwrappedOptional<Swift.String> at SwiftTestClass.swift

在輸出中找到Summary這個(gè)單詞.這里有兩個(gè)有趣的事情需要注意.
首先, 有兩個(gè)symbols被發(fā)現(xiàn)了.第一個(gè)和第二個(gè)有著同樣的名字;然而, 第一個(gè)有@objc的前綴. 這是編譯器加上去的特定的函數(shù)是用來(lái)作為一個(gè)橋接函數(shù)的. 這有助于swift和Objective-C的混編.
第二, 你看到了函數(shù)名字是多么的長(zhǎng)嗎?要?jiǎng)?chuàng)建一個(gè)有效的Swift斷點(diǎn)就需要這么一個(gè)完整的名字.

如果你想在這個(gè)setter方法中設(shè)置一個(gè)斷點(diǎn), 你需要按照下面的方法做:

 (lldb) b Signals.SwiftTestClass.name.setter :
Swift.ImplicitlyUnwrappedOptional<Swift.String>

使用正則表達(dá)式可以減少大量的輸入.
撇開(kāi)你產(chǎn)生的swift函數(shù)名的長(zhǎng)度不說(shuō), 注意一下Swift的屬性的形式.在屬性name函數(shù)的簽名中單詞setter緊跟在屬性的后面. 也許getter方法也有著同樣的格式?
嘗試捕獲一下SwiftTestClass類(lèi)的name屬性的gettersetter方法, 與此同時(shí), 使用下面的正則表達(dá)式來(lái)查詢(xún):

(lldb) image lookup -rn Signals.SwiftTestClass.name

這條命令使用正則表達(dá)式查詢(xún)并提取Signals.SwiftTestClass.name中的一切.
由于這是一個(gè)正則表達(dá)式, 所以.是一個(gè)通配符, 同時(shí)在函數(shù)簽名單重又用來(lái)匹配點(diǎn)語(yǔ)法.
你會(huì)得到一些合理的輸出, 但是每一次在控制臺(tái)的輸出中你都會(huì)看到Summary.你會(huì)發(fā)現(xiàn)輸出匹配了getter, (Signals.SwiftTestClass.name.getter) , setter,(Signals.SwiftTestClass.name.setter), @objc對(duì)應(yīng)的橋接符, 以及一個(gè)包含materializeForSet的方法, 這個(gè)方法你再后面會(huì)學(xué)到.
下面是Swift屬性函數(shù)名的樣本:

ModuleName.Classname.PropertyName.(getter|setter)

提取方法,找到樣本和縮小你搜索范圍的能力, 是在你代碼中創(chuàng)建智能斷點(diǎn)解開(kāi)Swift/Objective-C語(yǔ)言?shī)W秘的非常偉大的途徑.

最后...創(chuàng)建斷點(diǎn)

現(xiàn)在你已經(jīng)知道了如何在你的代碼中查詢(xún)已經(jīng)存在的函數(shù)和方法, 是時(shí)候在這些方法上創(chuàng)建斷點(diǎn)了.
如果你的Signals項(xiàng)目正在運(yùn)行, 停止并重啟這個(gè)程序, 然后點(diǎn)擊暫停按鈕停止應(yīng)用程序并進(jìn)入LLDB控制臺(tái).
有幾種不同的方法創(chuàng)建斷點(diǎn).最簡(jiǎn)單的方法是輸入小寫(xiě)字母b緊跟著后面是函數(shù)名.這在Objective-C 和 C當(dāng)中非常的簡(jiǎn)單, 因?yàn)樗拿趾芏桃埠苋菀纵斎?例如, -[NSObject init]). 而在C++和swift中卻十分復(fù)雜, 因?yàn)榫幾g器會(huì)為函數(shù)返回給你一個(gè)相當(dāng)長(zhǎng)的名字.
鑒于UIKit主要是用Objective-C(至少在寫(xiě)這本書(shū)的時(shí)候是這樣實(shí)現(xiàn)的), 嘗試用 b參數(shù)創(chuàng)建一個(gè)斷點(diǎn), 像下面這樣:

(lldb) b -[UIViewController viewDidLoad]

你會(huì)看到下面這些輸出:

 Breakpoint 1: where = UIKit`-[UIViewController viewDidLoad], address =
0x0000000102bbd788

每當(dāng)你創(chuàng)建了一個(gè)有效的斷點(diǎn), 控制臺(tái)都會(huì)輸出一些關(guān)于這個(gè)斷點(diǎn)的信息.
在這次特定的情況下, 這個(gè)斷點(diǎn)作為Breakpoint 1被創(chuàng)建, 因?yàn)樵谶@個(gè)特定的調(diào)試會(huì)話(huà)中這是創(chuàng)建的第一個(gè)斷點(diǎn).當(dāng)你創(chuàng)建更多斷點(diǎn)的時(shí)候, 斷點(diǎn)ID會(huì)不斷的增長(zhǎng).
繼續(xù)運(yùn)行調(diào)試器, 一旦你繼續(xù)執(zhí)行一個(gè)新的SIGSTOP會(huì)被顯示出來(lái).在單元格上點(diǎn)擊進(jìn)入詳情的UIViewController.在詳情視圖控制器調(diào)用viewDidLoad的時(shí)候程序應(yīng)該會(huì)暫停.

注意:像許多快捷命令一樣, `b`是LLDB命令中一個(gè)長(zhǎng)單詞的縮寫(xiě). 你可以自己運(yùn)行help b 命令查看它的幫助, 并學(xué)習(xí)它很酷的技巧.

除了b命令以外,還有一個(gè)長(zhǎng)的breakpoint set命令, 這個(gè)命令有很多可用的選項(xiàng). 你在后面的幾個(gè)章節(jié)中會(huì)用到它. 許多命令都是breakpoint set命令的分支.

正則斷點(diǎn)和范圍

另一個(gè)極其有用的命令是正則表達(dá)式斷點(diǎn)rbreakbreakpoint set -r %1.你可以用智能的正則表達(dá)式斷點(diǎn)快速的在你想要停下來(lái)的許多地方創(chuàng)建斷點(diǎn).
讓我們回頭看看之前的很長(zhǎng)的那個(gè)swift屬性name函數(shù)的例子, 與下面的輸入不同的是:

 (lldb) b Breakpoints.SwiftTestClass.name.setter :
Swift.ImplicitlyUnwrappedOptional<Swift.String>

你可以輸入:

(lldb) rb SwiftTestClass.name.setter

盡管上面的命令更短一點(diǎn), 但是也有一個(gè)煩人的地方.這個(gè)斷點(diǎn)也會(huì)捕捉到Objective-C橋接的name屬性的setter方法, 當(dāng)這個(gè)方法被調(diào)用的時(shí)候, 會(huì)強(qiáng)制停止兩次.
你可以給這個(gè)斷點(diǎn)添加一個(gè)參數(shù)^(@).*, 這個(gè)參數(shù)的本質(zhì)是說(shuō)"過(guò)濾掉以@xxx開(kāi)始的函數(shù)".在未來(lái)的幾個(gè)章節(jié)中, 你將構(gòu)建一個(gè)執(zhí)行正則搜索的命令自動(dòng)過(guò)濾掉這些橋接函數(shù).
從現(xiàn)在開(kāi)始你只需要處理兩個(gè)斷點(diǎn). 另一種更加簡(jiǎn)潔的寫(xiě)法是, 下面這種形式:

(lldb) rb name\.setter

這會(huì)在任何包含name.setter的地方創(chuàng)建一個(gè)斷點(diǎn).這在你知道項(xiàng)目里沒(méi)有其他地方swift屬性的明知叫做name的時(shí)候是行的通的.否則的話(huà)你就會(huì)在在每一處都創(chuàng)建一個(gè)斷點(diǎn).
現(xiàn)在嘗試在UIViewController的每一個(gè)Objective-C 的實(shí)例方法里創(chuàng)建一個(gè)斷點(diǎn). 我們可以在LLDB中輸入下面的指令:

(lldb) rb '\-\[UIViewController\ '

反斜杠是轉(zhuǎn)義字符用來(lái)指明你再正則表達(dá)式中想要搜索的原意字符.那么結(jié)果就是, 這條指令會(huì)在每一個(gè)字符串里包含-[UIViewController后面跟著一個(gè)空格的地方停下來(lái).
等一下...在Objective-C的categories里是怎樣的呢?他們提供了一種(-|+)[ClassName(categoryName) method]的形式.你可以用包含categories的形式重寫(xiě)正則表達(dá)式.
在LLDB會(huì)話(huà)中輸入以下命令, 并在提示用輸入y進(jìn)行確認(rèn):

(lldb) breakpoint delete

這條命令會(huì)刪除你設(shè)置的所有的斷點(diǎn).
然后輸入下面的內(nèi)容:

(lldb) rb '\-\[UIViewController(\(\w+\))?\ '

這條命令在斷點(diǎn)的UIViewController后面在空格前面提供了一個(gè)包含一個(gè)或多個(gè)字符的括號(hào)選項(xiàng).
正則表達(dá)式斷點(diǎn)讓你用一個(gè)表達(dá)式捕獲一個(gè)寬泛的斷點(diǎn).
你可以用-f選項(xiàng)來(lái)將斷點(diǎn)指定在特定的文件中.

(lldb) rb . -f DetailViewController.swift

這在你調(diào)試DetailViewController.swift的時(shí)候很有用.它會(huì)在這個(gè)文件的所有屬性的getters/setters, blocks/closures, extensions/ categories, 和 functions/methods出打下斷點(diǎn).-f選項(xiàng)是用來(lái)限定范圍的.
如果你徹底瘋了或者想要折磨一下自己, 你可以想下面這樣簡(jiǎn)單的指定一下范圍:

(lldb) rb .

這會(huì)在所有的地方創(chuàng)建一個(gè)斷點(diǎn)...是的, 是所有地方!這會(huì)在Signals項(xiàng)目的所有代碼里創(chuàng)建斷點(diǎn), 所有UIKit的代碼和所有Foundation的代碼, 所有運(yùn)行循環(huán)的代碼.結(jié)果是, 你需要在調(diào)試器中不停的輸入continue才能繼續(xù)執(zhí)行.
還有另外一種方式來(lái)指定搜索的范圍.你可以用-s選項(xiàng)將范圍限制在單一的庫(kù)中:

(lldb) rb . -s Commons

這會(huì)在Commons庫(kù)中的每一個(gè)地方設(shè)置一個(gè)斷點(diǎn), CommonsSignals項(xiàng)目中包含的一個(gè)動(dòng)態(tài)庫(kù).
你也可以用同樣的方法在UIKit的每一個(gè)函數(shù)里創(chuàng)建斷點(diǎn):

(lldb) rb . -s UIKit

這顯然太瘋狂了.在iOS10.0系統(tǒng)里UIKit里大概有66,189個(gè)方法.如何在UIKit里只在你第一次觸發(fā)這個(gè)斷點(diǎn)的時(shí)候停下來(lái)呢?-o選項(xiàng)提供了一個(gè)解決方案.它創(chuàng)建了one-shot型的斷點(diǎn). 這種斷點(diǎn)在觸發(fā)一次后就會(huì)自動(dòng)刪除. 因此只會(huì)觸發(fā)一次.
要看這種方法的效果, 可以在LLDB會(huì)話(huà)中輸入下面的命令:

(lldb) breakpoint delete
(lldb) rb . -s UIKit -o
注意:當(dāng)LLDB執(zhí)行這些命令的時(shí)候請(qǐng)耐心等待, 因?yàn)長(zhǎng)LDB創(chuàng)建了大量的斷點(diǎn).并且要確保你使用的是模擬器, 否則的話(huà)你將會(huì)等待非常長(zhǎng)的時(shí)間.

接下來(lái), 讓調(diào)試器繼續(xù)執(zhí)行, 并在tableView中的一個(gè)cell上點(diǎn)擊一下.調(diào)試器就會(huì)在UIKit的方法第一次調(diào)用的時(shí)候停下來(lái).最后, 繼續(xù)運(yùn)行調(diào)試器, 并且這個(gè)斷點(diǎn)將不會(huì)再次觸發(fā).

修改和刪除斷點(diǎn)

現(xiàn)在你已經(jīng)對(duì)如何創(chuàng)建斷點(diǎn)有了基本的理解, 你可能還會(huì)想知道你如何改變它們. 當(dāng)你想要修改, 刪除或者暫時(shí)停用一個(gè)斷點(diǎn)的時(shí)候該怎么做?當(dāng)你想讓斷點(diǎn)下次觸發(fā)的時(shí)候執(zhí)行一個(gè)特定的命令你又該怎么做呢?
首先, 你需要知道怎樣唯一的標(biāo)記一個(gè)或者一組斷點(diǎn). 在你創(chuàng)建斷點(diǎn)的時(shí)候你也可以用-N 選項(xiàng)命名一個(gè)斷點(diǎn)...用數(shù)字做標(biāo)記可能真的不適合你.
構(gòu)建并運(yùn)行APP來(lái)清空LLDB會(huì)話(huà).接下來(lái), 暫停調(diào)試器并在LLDB會(huì)話(huà)中輸入一下命令:

(lldb) b main

你可能會(huì)看到下面這些輸出:

Breakpoint 1: 20 locations.

這句話(huà)的是意思是說(shuō)你再不同模塊中的與main匹配的20個(gè)地方創(chuàng)建了斷點(diǎn).
在這種情況下, 這個(gè)斷點(diǎn)的的ID是1, 因?yàn)樗悄銊?chuàng)建的第一個(gè)斷點(diǎn). 要查看這個(gè)斷點(diǎn)的詳細(xì)信息, 你可以用breakpoint list命令.輸入下面的內(nèi)容:

(lldb) breakpoint list 1

應(yīng)該會(huì)輸出下面類(lèi)似的內(nèi)容:

1: name = 'main', locations = 20, resolved = 20, hit count = 0
  1.1: where = Breakpoints`main + 22 at AppDelegate.swift:12, address =
0x00000001057676e6, resolved, hit count = 0
  1.2: where = Foundation`-[NSThread main], address = 0x000000010584d182,
resolved, hit count = 0
  1.3: where = Foundation`-[NSBlockOperation main], address =
0x000000010585df4a, resolved, hit count = 0
  1.4: where = Foundation`-[NSFilesystemItemRemoveOperation main],
address = 0x00000001058990ff, resolved, hit count = 0
  1.5: where = Foundation`-[NSFilesystemItemMoveOperation main], address
= 0x0000000105899c23, resolved, hit count = 0
1.6: where = Foundation`-[NSInvocationOperation main], address =
0x00000001058c4fb9, resolved, hit count = 0
  1.7: where = Foundation`-[NSDirectoryTraversalOperation main], address
= 0x000000010590a87f, resolved, hit count = 0
  1.8: where = Foundation`-[NSOperation main], address =
0x000000010595209c, resolved, hit count = 0
  1.9: where = UIKit`-[UIStatusBarServerThread main], address =
0x00000001068b84f0, resolved, hit count = 0
  1.10: where = UIKit`-[_UIDocumentActivityItemProvider main], address =
0x000000010691898c, resolved, hit count = 0
  1.11: where = UIKit`-[_UIDocumentActivityDownloadOperation main],
address = 0x0000000106975d51, resolved, hit count = 0
  1.12: where = UIKit`-[_UIGetAssetThread main], address =
0x000000010698ef4d, resolved, hit count = 0
  1.13: where = UIKit`-[UIWebPDFSearchOperation main], address =
0x0000000106ae7c99, resolved, hit count = 0
  1.14: where = UIKit`-[UIActivityItemProvider main], address =
0x0000000106c4e525, resolved, hit count = 0
  1.15: where = MobileCoreServices`-[LSOpenOperation main], address =
0x000000010879703c, resolved, hit count = 0
  1.16: where = ImageIO`main, address = 0x000000010c87535d, resolved, hit
count = 0
  1.17: where = AppSupport`-[_CPPowerAssertionThread main], address =
0x000000010ed95f03, resolved, hit count = 0
  1.18: where = AppSupport`-[CPDistributedMessagingAsyncOperation main],
address = 0x000000010ed9ba53, resolved, hit count = 0
  1.19: where = JavaScriptCore`WTF::RunLoop::main(), address =
0x0000000111c68af0, resolved, hit count = 0
  1.20: where = ConstantClasses`main, address = 0x0000000114329cd2,
resolved, hit count = 0

這里顯示了這個(gè)斷點(diǎn)的詳細(xì)內(nèi)容, 包含所有包含main這個(gè)單詞的位置.
顯示這些信息的簡(jiǎn)介內(nèi)容的方式是輸入下面的內(nèi)容:

(lldb) breakpoint list 1 -b

這會(huì)輸出一些簡(jiǎn)潔的更可視化的信息.如果你有一個(gè)斷點(diǎn)的ID, 并且這個(gè)ID里包含著多個(gè)斷點(diǎn), 這個(gè)簡(jiǎn)明的標(biāo)志是一個(gè)好的解決方案.
如果你想要查看LLDB中所有的斷點(diǎn), 只需要簡(jiǎn)單的輸入下面的內(nèi)容:

(lldb) breakpoint list

你也可以指名多個(gè)斷點(diǎn)的ID和范圍:

(lldb) breakpoint list 1 3
(lldb) breakpoint list 1-3

breakpoint delete命令刪除所有的斷點(diǎn)是一個(gè)重量級(jí)的操作.你可以簡(jiǎn)單的使用斷點(diǎn)的ID來(lái)刪除一個(gè)斷點(diǎn)或者斷點(diǎn)的集合:
你可以用指定斷點(diǎn)ID的方式刪除一個(gè)斷點(diǎn):

(lldb) breakpoint delete 1

然而, "main"斷點(diǎn)里面包含了20個(gè)斷點(diǎn).你也可以用下面這總方式刪除一個(gè)斷點(diǎn):

(lldb) breakpoint delete 1.1

這會(huì)刪除1斷點(diǎn)的第一個(gè)自斷點(diǎn).

我們?yōu)槭裁匆獙W(xué)這些?

在本章節(jié)中你學(xué)到了大量的內(nèi)容. 斷點(diǎn)是一個(gè)很大的話(huà)題并且對(duì)調(diào)試專(zhuān)家來(lái)說(shuō)是一門(mén)主要的藝術(shù)用來(lái)快速的發(fā)現(xiàn)并找到實(shí)物本質(zhì).你也已經(jīng)了解了如何用正則表達(dá)式來(lái)搜索代碼.現(xiàn)在是時(shí)候梳理一下正則表達(dá)式的語(yǔ)法了, 在本書(shū)的后面你將會(huì)用到大量的正則表達(dá)式.
瀏覽 https://docs.python.org/2/library/re.html 來(lái)學(xué)習(xí)正則表達(dá)式.并嘗試找出如何創(chuàng)建不區(qū)分大小寫(xiě)的正則表達(dá)式.
現(xiàn)在你只不過(guò)是出不得了解了一下編譯器是如何生成Objective-C 和 Swift的函數(shù)的.嘗試找出如何在Objective-C的blocks或者Swift的closures里創(chuàng)建斷點(diǎn)的代碼.如果你做到了, 嘗試設(shè)計(jì)出一個(gè)斷點(diǎn)只在Signals項(xiàng)目里的Commonsframework的Objective-C blocks里停止的斷點(diǎn).這些都是你再未來(lái)創(chuàng)建更復(fù)雜的斷點(diǎn)所需要的技能.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市莺丑,隨后出現(xiàn)的幾起案子简珠,更是在濱河造成了極大的恐慌茁计,老刑警劉巖奸晴,帶你破解...
    沈念sama閱讀 216,843評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瞎颗,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡藏研,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)概行,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)蠢挡,“玉大人,你說(shuō)我怎么就攤上這事凳忙∫堤ぃ” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,187評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵涧卵,是天一觀的道長(zhǎng)勤家。 經(jīng)常有香客問(wèn)我,道長(zhǎng)柳恐,這世上最難降的妖魔是什么伐脖? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,264評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮乐设,結(jié)果婚禮上讼庇,老公的妹妹穿的比我還像新娘。我一直安慰自己近尚,他們只是感情好蠕啄,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,289評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著戈锻,像睡著了一般歼跟。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上舶沛,一...
    開(kāi)封第一講書(shū)人閱讀 51,231評(píng)論 1 299
  • 那天嘹承,我揣著相機(jī)與錄音,去河邊找鬼如庭。 笑死叹卷,一個(gè)胖子當(dāng)著我的面吹牛撼港,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播骤竹,決...
    沈念sama閱讀 40,116評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼帝牡,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了蒙揣?” 一聲冷哼從身側(cè)響起靶溜,我...
    開(kāi)封第一講書(shū)人閱讀 38,945評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎懒震,沒(méi)想到半個(gè)月后罩息,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,367評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡个扰,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,581評(píng)論 2 333
  • 正文 我和宋清朗相戀三年瓷炮,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片递宅。...
    茶點(diǎn)故事閱讀 39,754評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡娘香,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出办龄,到底是詐尸還是另有隱情烘绽,我是刑警寧澤,帶...
    沈念sama閱讀 35,458評(píng)論 5 344
  • 正文 年R本政府宣布俐填,位于F島的核電站安接,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏玷禽。R本人自食惡果不足惜赫段,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,068評(píng)論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望矢赁。 院中可真熱鬧糯笙,春花似錦、人聲如沸撩银。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,692評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)额获。三九已至够庙,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間抄邀,已是汗流浹背耘眨。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,842評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留境肾,地道東北人剔难。 一個(gè)月前我還...
    沈念sama閱讀 47,797評(píng)論 2 369
  • 正文 我出身青樓胆屿,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親偶宫。 傳聞我的和親對(duì)象是個(gè)殘疾皇子非迹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,654評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容