NSLog,po命令和普通斷點(diǎn)調(diào)試相信每個(gè)iOS開(kāi)發(fā)者都會(huì),這里就不作介紹了。
一、Memory Graph
Xcode8新增:Memory Graph解決閉包引用循環(huán)問(wèn)題

這個(gè)時(shí)候就進(jìn)入了斷點(diǎn)模式,可以查看issue面板,注意選擇右邊Runtime:

有很多嘆號(hào)說(shuō)明就有問(wèn)題了。看內(nèi)存中object的名字,有一條是Closure captures leaked。展開(kāi)后點(diǎn)擊就可以看到這個(gè)issue對(duì)應(yīng)的內(nèi)存圖形展示在中間的面板中。當(dāng)然了,我們更多的時(shí)候是在debug頁(yè)面下查看:

所以,這里面引用循環(huán)了。點(diǎn)擊紫色的嘆號(hào)會(huì)出現(xiàn)Xcode分析出來(lái)的內(nèi)存引用圖形:

有了這個(gè)圖就很容易看出來(lái)了:這里有個(gè)引用循環(huán)
順便提一下,通過(guò)在Arguments中設(shè)置參數(shù),打印出App加載的時(shí)長(zhǎng),包括整體加載時(shí)長(zhǎng),動(dòng)態(tài)庫(kù)加載時(shí)長(zhǎng)等。
在Environment Variables中添加DYLD_PRINT_STATISTICS字段,并設(shè)置為YES,在控制臺(tái)就會(huì)打印加載時(shí)長(zhǎng)。


二、Xcode調(diào)試技巧之:LLDB
1、po:print object的縮寫(xiě),表示顯示對(duì)象的文本描述,如果對(duì)象不存在則打印nil。
簡(jiǎn)單的打印一個(gè)對(duì)象我們就不說(shuō)了,我們來(lái)說(shuō)說(shuō)特殊的應(yīng)用場(chǎng)景吧!
應(yīng)用場(chǎng)景:你想知道一個(gè)視圖包含了哪些子視圖。當(dāng)然你可以循環(huán)打印子視圖,但是下面只需要一個(gè)命令即可解決。
輸出視圖層級(jí)關(guān)系(這是一個(gè)被隱藏的命令):
po [[self view] recursiveDescription]
還有個(gè)常見(jiàn)的調(diào)試場(chǎng)景,比如你要打印一個(gè)model。你直接用NSLog或po對(duì)象處理的結(jié)果是model的地址,這并不是我們想要的。怎么辦?有沒(méi)有解決方法呢?
答案是有的。你可以重寫(xiě)model里面的description方法。但是,如果model里屬性非常多,這樣就不適用了。你不可能說(shuō)在description方法里面拼接屬性返回。這樣不僅麻煩,而且可讀性非常差。到這里,我們可以利用runtime動(dòng)態(tài)獲取屬性并返回。不過(guò)我并不建議你重寫(xiě)description方法,我推薦你重寫(xiě)debugDescription方法(至于詳細(xì)的介紹以及如何重寫(xiě)請(qǐng)點(diǎn)擊此處),因?yàn)閐ebugDescription方法和description方法效果一樣,區(qū)別在于debugDescription方法是在你使用po命令時(shí)調(diào)用的,實(shí)際上也是調(diào)用了description方法。
2、p:可以用來(lái)打印基本數(shù)據(jù)類(lèi)型。
3、call:執(zhí)行一段代碼
call NSLog(@
"%@"
, @
"yang"
)
4、expr:動(dòng)態(tài)執(zhí)行指定表達(dá)式
expr i = 101
輸出:(int)$0 = 101
5、bt:打印當(dāng)前線程堆棧信息
如果要打印所以線程堆棧信息,使用:bt all即可。
6、image:常用來(lái)尋找棧地址對(duì)應(yīng)代碼位置:
舉個(gè)栗子:
應(yīng)用場(chǎng)景(數(shù)組越界)模擬代碼:
NSArray *array = @[@"yang",@"she",@"bing"];
NSLog(@"%@",array[3]);
錯(cuò)誤信息如下:
*** Terminating app due to uncaught exception
'NSRangeException'
, reason:
'*** -[__NSArrayI objectAtIndex:]: index 3 beyond bounds [0 .. 2]'
*** First
throw
call stack:
(
0 CoreFoundation 0x000000010579734b __exceptionPreprocess + 171
1 libobjc.A.dylib 0x00000001051f821e objc_exception_throw + 48
2 CoreFoundation 0x00000001056d1eeb -[__NSArrayI objectAtIndex:] + 155
3 BGMultimediaDemo 0x0000000104c25550 -[ViewController viewDidLoad] + 192
4 UIKit 0x0000000105d5c06d -[UIViewController loadViewIfRequired] + 1258
......
......
......
21 BGMultimediaDemo 0x0000000104c25adf main + 111
22 libdyld.dylib 0x000000010857268d start + 1
23 ??? 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating
with
uncaught exception of type NSException
這個(gè)時(shí)候我們?nèi)绻麘岩沙鲥e(cuò)的地址是0x0000000104c25550,那么我們可以使用下面命令來(lái)找出錯(cuò)誤代碼的位置:
image lookup --address 0x0000000104c25550
執(zhí)行命令后輸出結(jié)果如下:
Address: BGMultimediaDemo[0x0000000100001550] (BGMultimediaDemo.__TEXT.__text + 192)
Summary: BGMultimediaDemo`-[ViewController viewDidLoad] + 192 at ViewController.m:30
從上面輸出結(jié)果中可以看出,錯(cuò)誤位置應(yīng)該是ViewController.m文件中的30行。是不是超級(jí)好用?反正我覺(jué)得好用。
三、Xcode調(diào)試技巧之:斷點(diǎn)(Breakpoint)
斷點(diǎn),程序員Debug必備技之一。
1、條件斷點(diǎn)
打上斷點(diǎn)之后,對(duì)斷點(diǎn)進(jìn)行編輯,設(shè)置相應(yīng)過(guò)濾條件。下面簡(jiǎn)單的介紹一下條件設(shè)置:
Condition:返回一個(gè)布爾值,當(dāng)布爾值為真觸發(fā)斷點(diǎn),一般里面我們可以寫(xiě)一個(gè)表達(dá)式。
Ignore:忽略前N次斷點(diǎn),到N+1次再觸發(fā)斷點(diǎn)。
Action:斷點(diǎn)觸發(fā)事件,分為六種:
AppleScript:執(zhí)行腳本。
Capture GPU Frame:用于OpenGL ES調(diào)試,捕獲斷點(diǎn)處GPU當(dāng)前繪制幀。
Debugger Command:和控制臺(tái)中輸入LLDB調(diào)試命令一致。
Log Message:輸出自定義格式信息至控制臺(tái)。
Shell Command:接收命令文件及相應(yīng)參數(shù)列表,Shell Command是異步執(zhí)行的,只有勾選“Wait until done”才會(huì)等待Shell命令執(zhí)行完在執(zhí)行調(diào)試。
Sound:斷點(diǎn)觸發(fā)時(shí)播放聲音。
這些功能平時(shí)在調(diào)試程序的過(guò)程中都可以進(jìn)行嘗試,說(shuō)實(shí)話我用的設(shè)置Condition項(xiàng)會(huì)較多些。
Options(Automatically continue after evaluating actions選項(xiàng)):選中后,表示斷點(diǎn)不會(huì)終止程序的運(yùn)行。

2、異常斷點(diǎn)
異常斷點(diǎn)可以快速定位不滿足特定條件的異常,比如常見(jiàn)的數(shù)組越界,這時(shí)候很難通過(guò)異常信息定位到錯(cuò)誤所在位置。這個(gè)時(shí)候異常斷點(diǎn)就可以發(fā)揮作用了。
添加異常斷點(diǎn):

編輯異常斷點(diǎn):

Exception:可以選擇拋出異常對(duì)象類(lèi)型:OC或C++。
Break:選擇斷點(diǎn)接收的拋出異常來(lái)源是Throw還是Catch語(yǔ)句。
3、符號(hào)斷點(diǎn)
符號(hào)斷點(diǎn)的創(chuàng)建方式和異常斷點(diǎn)一樣一樣的,在符號(hào)斷點(diǎn)中可以指定要中斷執(zhí)行的方法:
舉個(gè)例子,常見(jiàn)的場(chǎng)景,我想讓它執(zhí)行到ViewController類(lèi)中的viewWillAppear方法就中斷執(zhí)行:

Symbol:[ViewController viewWillAppear:]即[類(lèi)名 方法名]可以執(zhí)行到指定類(lèi)的指定方法中開(kāi)始斷點(diǎn)。如果只有viewWillAppear:即方法名,它會(huì)執(zhí)行到所以類(lèi)中的viewWillAppear:方法中開(kāi)始斷點(diǎn)。
4、監(jiān)控?cái)帱c(diǎn)
我們調(diào)試程序的大部分時(shí)候都是為了監(jiān)控某個(gè)變量的變化,在代碼中變量出現(xiàn)的地方添加斷點(diǎn)不僅累而且還可能漏掉,事后還得一個(gè)一個(gè)刪掉,實(shí)在很累。
我們可以通過(guò)為變量添加監(jiān)控?cái)帱c(diǎn)來(lái)簡(jiǎn)單地做到這一點(diǎn)。找到變量第一次出現(xiàn)的地方,添加一個(gè)普通斷點(diǎn),進(jìn)入 debug 模式后在 Variables View 中右鍵變量,選擇 Watch 變量名。這樣,每一次該變量被改變都會(huì)觸發(fā)斷點(diǎn)告知我們。
四、Xcode調(diào)試技巧之:EXC_BAD_ACCESS
1、開(kāi)啟僵尸對(duì)象
開(kāi)啟Zombie模式之后會(huì)導(dǎo)致內(nèi)存上升,因?yàn)樗砸呀?jīng)被釋放(引用計(jì)數(shù)為0)的對(duì)象被僵尸對(duì)象取代,并未真的釋放掉。這個(gè)時(shí)候再給僵尸對(duì)象發(fā)送消息,就會(huì)拋出異常,并打印出異常信息,你可以輕松的找到錯(cuò)誤代碼位置,結(jié)束Zombies時(shí)會(huì)釋放。它的主要功能是檢測(cè)野指針調(diào)用。
使用方法:
“Edit Scheme…” —> “Run” —> “Diagnostics” —> “Zombie Objects”
打開(kāi)”Edit Scheme…”窗口:

開(kāi)啟Zombie模式:

注意:Zombie模式不能再真機(jī)上使用,只能在模擬器上使用。
2、Address Sanitizer(地址消毒劑)
在Xcode7之后新增了AddressSanitizer工具,為我們調(diào)試EXC_BAD_ACCESS錯(cuò)誤提供了便利。當(dāng)程序創(chuàng)建變量分配一段內(nèi)存時(shí),將此內(nèi)存后面的一段內(nèi)存也凍結(jié)住,標(biāo)識(shí)為中毒內(nèi)存。程序訪問(wèn)到中毒內(nèi)存時(shí)(訪問(wèn)越界),立即中斷程序,拋出異常并打印異常信息。你可以根據(jù)中斷位置及輸出的Log信息來(lái)解決錯(cuò)誤。當(dāng)然,如果變量已經(jīng)釋放了,它所占用的內(nèi)存也會(huì)被標(biāo)識(shí)為中毒內(nèi)存,這個(gè)時(shí)候訪問(wèn)這片內(nèi)存空間同樣會(huì)拋出異常。
使用方法:
“Edit Scheme…” —> “Run” —> “Diagnostics” —> “Address Sanitizer”

開(kāi)啟AddressSanitizer之后,在調(diào)試程序的過(guò)程中,如果有遇到EXC_BAD_ACCESS錯(cuò)誤,程序則會(huì)自動(dòng)終端,拋出異常。
3.Analyze分析器
Analyze分析器是一種靜態(tài)的工具,可以對(duì)我們的程序進(jìn)行分析,找出我們未使用的變量,或一些死存儲(chǔ)。執(zhí)行Analyze如下:Product-->Analyze. 如下藍(lán)色的標(biāo)記就是靜態(tài)分析的結(jié)果。


當(dāng)然,我們可以設(shè)置在編譯程序的時(shí)候同時(shí)Analyze,把下列選項(xiàng)設(shè)為Yes即可。

5、視圖調(diào)試
如今iOS開(kāi)發(fā)的UI設(shè)計(jì)有很多種方式,比如storyboard,xib,代碼實(shí)現(xiàn)。對(duì)于stoayboard,xib可視化實(shí)現(xiàn)是比較簡(jiǎn)單的,但是對(duì)于一些“iOS老程序員”而言,都喜歡使用代碼實(shí)現(xiàn)UI,并且可能UI層次還比較復(fù)雜。這樣就給我們新接手項(xiàng)目的開(kāi)發(fā)者帶來(lái)很多困擾。如何快速查看一個(gè)復(fù)雜UI的界面層次和布局,最快的方法就是用到視圖調(diào)試。
當(dāng)項(xiàng)目運(yùn)行到某一個(gè)界面(可以是模擬器或真機(jī))時(shí),開(kāi)啟視圖調(diào)試,點(diǎn)擊按鈕如圖:

這樣就會(huì)進(jìn)入試圖調(diào)試,你可以很方便的查看這個(gè)界面。這里可以看到控件之間的層次關(guān)系。
六、參考博客地址:
instruments這里不做介紹,請(qǐng)參考:
instruments來(lái)檢驗(yàn)?zāi)愕腶pp
Memory Graph
iOS 開(kāi)發(fā)調(diào)試技巧
