編寫代碼過程當中出現錯誤、異常是不可避免的。一般咱們都須要進行大量的調試去尋找、解決問題。這時,熟練掌握調試技巧將很大程度上的提升工做效率。接下來就說說開發過程當中Xcode的調試方法。html
1. Enable NSZombie Objects (開啓殭屍對象)。
這個技巧主要用來追終重複釋放的問題。我的認爲,ARC推出以來。項目的基本是基於ARC環境。不用開發者主動去調用release去釋放對象,因此不用太在乎這個方法。這裏就很少作介紹了。想了解該方法的同窗請 坐飛機
2. 斷點調試(全局斷點、條件斷點)
1、全局斷點:node
NSArray *aa = @[@2,@4]; NSLog(@"%@",aa[3]);
這兩行代碼,沒有添加全局斷點時,運行crash,直接就跳到了mian函數,以下圖:ios
接下來添加全局斷點,方法以下圖:c++
添加以後運行,奔潰後,程序停留在了crash那行代碼。程序員
是否是很方便,很省事。哈哈!(ps 不過有的crash,這種方式定位不到)express
2、條件斷點:設置斷點觸發的條件,方便開發者對特定狀況進行調試
以下圖:
在for循環中添加一個斷點。右擊斷點選擇」Edit BreakPoint」,而後設置斷點觸發條件。
數組
這個例子當 「i==5」時,斷點觸發,以下圖:
xcode
3. Static Analyzer (靜態分析)
Static Analyzer主要用於分析內存,避免內存泄漏。主要對如下狀況進行分析。
未使用的實例變量、未初始化的實例變量、類型不兼容、沒法達到的路徑、引用空指針
使用:command + shift +B,以下圖就能輕鬆找到可能內存泄漏的代碼,而後咱們根據代碼環境進行修復就能夠了(ps:有的內存泄漏可能檢測不出來,仍是須要咱們在寫代碼時對內存這塊多留點心。)網絡
4. LLDB調試器
這個方法是我今天主推的方法。比較高級,也更加靈活、方便。
隨着Xcode5,LLDB調試器已經取代了GDB,成爲了Xcode工程中默認的調試器。其實Xcode已經幫咱們完成了大部分工做,並且不少東西也能夠在Xcode中直接看到。因此這裏咱們只列舉經常使用的命令。
打印:p,print的縮寫:該命令若是打印的是簡單類型則會列出簡單類型的的類型和值,若是是對象會打印出對象的地址。
po,print Object 的縮寫,用於輸出OC對象
以下如,當運行到斷點處時,控制檯就會出現LLDB的調試命令行。咱們只需在這裏進行調試。
閉包
expr:expression的縮寫,能夠在調試時動態執行指定表達式,並將結果打印出來。經常使用於在調試過程當中修改變量的值。
如上圖,你在控制檯輸入 expr a=2
你就能看到 (NSInteger) $11 = 2
這是a的值就被動態改爲了2
除此以外,還可使用這個命令生成一個新的對象,如: expr int $b = 0
p $b
這條命令用於輸出新申明對象的值(注意要加$)
image: image命令可用於尋址,有多個組合命令,在控制檯輸入help image
可查看image的用法。比較實用的用法是用於尋找棧地址對應的代碼位置,下面咱們來舉個例子:
NSArray *array = @[@1,@2]; NSLog(@"%@",array[2]);
這段代碼很明顯會crash,運行以後拋出下面的異常
2016-03-23 22:26:11.014 Test[3631:136626] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndex:]: index 2 beyond bounds [0 .. 1]' *** First throw call stack: ( 0 CoreFoundation 0x0000000104f28f45 __exceptionPreprocess + 165 1 libobjc.A.dylib 0x00000001049a2deb objc_exception_throw + 48 2 CoreFoundation 0x0000000104e17b14 -[__NSArrayI objectAtIndex:] + 164 3 Test 0x00000001044a5829 -[ViewController viewDidLoad] + 265 4 UIKit 0x0000000105467cc4 -[UIViewController loadViewIfRequired] + 1198 5 UIKit 0x0000000105468013 -[UIViewController view] + 27 6 UIKit 0x000000010534151c -[UIWindow addRootViewControllerViewIfPossible] + 61 7 UIKit 0x0000000105341c05 -[UIWindow _setHidden:forced:] + 282 8 UIKit 0x00000001053534a5 -[UIWindow makeKeyAndVisible] + 42 9 UIKit 0x00000001052cd396 -[UIApplication _callInitializationDelegatesForMainScene:transitionContext:] + 4131 10 UIKit 0x00000001052d39c3 -[UIApplication _runWithMainScene:transitionContext:completion:] + 1750 11 UIKit 0x00000001052d0ba3 -[UIApplication workspaceDidEndTransaction:] + 188 12 FrontBoardServices 0x0000000107c83784 -[FBSSerialQueue _performNext] + 192 13 FrontBoardServices 0x0000000107c83af2 -[FBSSerialQueue _performNextFromRunLoopSource] + 45 14 CoreFoundation 0x0000000104e55011 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17 15 CoreFoundation 0x0000000104e4af3c __CFRunLoopDoSources0 + 556 16 CoreFoundation 0x0000000104e4a3f3 __CFRunLoopRun + 867 17 CoreFoundation 0x0000000104e49e08 CFRunLoopRunSpecific + 488 18 UIKit 0x00000001052d04f5 -[UIApplication _run] + 402 19 UIKit 0x00000001052d530d UIApplicationMain + 171 20 Test 0x00000001044a5baf main + 111 21 libdyld.dylib 0x000000010764c92d start + 1 22 ??? 0x0000000000000001 0x0 + 1 ) libc++abi.dylib: terminating with uncaught exception of type NSException
如今我懷疑出錯的地址是0x00000001044a5829
(可根據執行文件名或最小的棧地址判斷)爲進一步精肯定位咱們可輸入如下命令image lookup --address 0x00000001044a5829
命令執行後返回結果以下:
Address: Test[0x0000000100001829] (Test.__TEXT.__text + 265) Summary: Test`-[ViewController viewDidLoad] + 265 at ViewController.m:21
由此,咱們能夠看出出錯的地方是ViewController.m文件的第21行。
咱們還可使用image lookup命令查看具體的類,以下:
(lldb) image lookup --type UIView
Best match found in /Users/jamalping/Library/Developer/Xcode/DerivedData/Test-gviuudbzlyhssmanjxpwhchdbscz/Build/Products/Debug-iphonesimulator/Test.app/Test:
id = {0x00001e8d}, name = "UIView", byte-size = 8, decl = UIView.h:144, clang_type = "@interface UIView : UIResponder @property ( getter = isUserInteractionEnabled,setter = setUserInteractionEnabled:,assign,readwrite,nonatomic ) BOOL userInteractionEnabled; @property ( getter = tag,setter = setTag:,assign,readwrite,nonatomic ) NSInteger tag; @property ( readonly,getter = layer,setter = <null selector>,nonatomic ) CALayer * layer; @property ( readonly,getter = isFocused,setter = <null selector>,nonatomic ) BOOL focused; @property ( getter = semanticContentAttribute,setter = setSemanticContentAttribute:,assign,readwrite,nonatomic ) UISemanticContentAttribute semanticContentAttribute; @end "
call
call:即調用,如咱們在viewDidLoad:
設置一個斷點,在程序中斷的時候輸入call self.view.backgroudColor = [UIColo redColor]
繼續運行程序,view就變成紅色了,在調試的時候靈活運用call命令能夠達到事半功倍的效果。
最近一直沒有更新簡書是由於在開發和測試階段,有任務,沒有進行學習,不過在作任務的時候也遇到了一些技術點,在這裏總結一下。
今天咱們來談一談�Xcode調試的技巧。�就像玩遊戲,有些玩家他們知道怎麼操做,會放技能會走路,可是他們不知道買裝備,玩了一局下來,鞋子小刀都沒有買,這樣行走江湖很危險啊!因此咱們出門要把裝配佩戴好,學會裝備本身才是王道!
本文將簡書和各大博客上邊涉及該方面的內容進行彙總,並試圖進行全方位的總結,若是有什麼不足之處還但願你們給提出來,我會進行補充和修改。如今咱們開始吧。
整體看來,關於調試的方法包括如下幾個:�日誌輸出&LLDB、斷點、性能、一些小技巧等幾個大的方面。咱們一一進行學習和總結。
嘗試接受新鮮事物和方法,方法都是熟能生巧的,各類方法綜合運用,用好了會事半功倍。
關於日誌輸出,咱們最早想到的是NSLog,可是弊端在於咱們須要在想要打印的位置添加NSLog代碼並從新運行項目,這樣耽誤時間,因此咱們平時用的比較多的是打斷點,而後po一下。這個「po」就是LLDB裏面的一句命令。
那麼,什麼是LLDB呢:它是一個有着 REPL 的特性和 C++ ,Python 插件的開源調試器。LLDB 綁定在 Xcode 內部,存在於主窗口底部的控制檯中。調試器容許你在程序運行的特定時暫停它,你能夠查看變量的值,執行自定的指令,而且按照你所認爲合適的步驟來操做程序的進展。咱們能夠簡單的理解成它是一個調試器。
(1)LLDB命令行
像下圖中,咱們打斷點後,控制檯右邊裏面會出現一個「lldb」,咱們平時不怎麼關注它,但咱們一直在使用它。
<1> help命令
斷點的時候,咱們在控制檯右邊lldb後邊輸入一個「help」,而後敲回車,就會看到全部關於lldb的命令以及各自的介紹,以下圖:
<2> print命令
print很好理解,就是打印,使用過程當中咱們能夠直接用p來代替print。
<3> expression命令
該命令能夠改變程序實際參數的值,目的是方便了調試:不用從新運行項目。例以下圖中,咱們簡單的作一個測試,令蘋果=1,橘子=2,all應該=3,在斷點過程當中,咱們用expression命令修改了橘子的值,令橘子=5,結果再打印all的時候,all=6(親測好使)。使用過程當中咱們能夠直接用 expr來代替expression。
這裏咱們注意到一個「$9」,這裏的9是咱們使用lldb命令的次數,例以下圖,咱們expression一次橘子,po了一次all,再print橘子的時候,顯示的是「$11」,說明咱們print命令是$10。這個不用管,只是提醒咱們一下而已,做用應該不是很大。
<4> po命令
如今咱們來看看平時用的比較多的「po」,它是「print object」的簡寫。po一下,咱們就能夠看到對象的詳細信息。po使用的比較多,使用起來也比較簡單、方便,這裏不作多餘的介紹。
<5> image命令
image list 查看工程中使用的庫
image lookup --address 0x000000010e0979ac 程序崩潰的時候定位,查看具體報錯位置
這個其實咱們能夠想辦法用在咱們崩潰日誌的收集裏面,這樣的話咱們就能夠直接定位到崩潰信息的具體位置了。(親測,很差使。感受xcode反饋的崩潰信息不許確。)
<6> call命令
call即調用的意思。上述的po和p也有調用的功能。因此通常只在不須要顯示輸出,或方法無返回值的時候使用call。 和上面的命令同樣,咱們依然在viewDidLoad:裏面設置斷點,而後在程序中斷的時候輸入下面的命令:
call [self.view setBackgroundColor:[UIColor redColor]]
繼續運行程序,看看view的背景顏色是否是變成紅色的了!在調試的時候靈活運用call命令能夠起到事半功倍的做用(親測好使)。
(2)LLDB調試欄
通常的按鈕和功能咱們用的比較多,也比較熟悉,這裏咱們着重介紹一下Debug View Hierachy和xcode8新增的memory graph功能。
<1>Debug View Hierachy
Debug View Hierachy,翻譯過來就是:調試視圖層次。除了點擊控制檯出的圖標,也能夠從菜單中選擇Debug > View Debugging > Capture View Hierarchy 來啓動視圖調試。(咱們能夠看出xcode開發人員的用心之處:重疊在一塊兒的長方形,咱們大概就明白這個按鈕是表示層級關係的)在斷點或者不是斷點的狀況下均可以經過點擊這個按鈕查看視圖層級關係。點擊按鈕,咱們會在xcode最頂端的地方看到下圖的一個信息:
capture user interface for YourAppName:capture是捕獲的意思,interface,face咱們知道是臉,inter是進入的意思,interface就是進入臉,咱們大概可以明白這句話的意思就是「爲你的app捕獲用戶交界面」。
從左到右控件排序:(上圖中也簡單解釋了各個功能)
調整視圖間距:調整不一樣視圖間的間距。
展現被剪切的內容:當前展現視圖中被剪切的部分。
展現約束:展現選中視圖的約束。
重置查看區域:將3D渲染透視圖恢復至默認狀態。
調整查看模式:選擇性地展現3D渲染透視圖,好比僅展現內容,僅展現框架以及同時展現內容和框架。
縮小:縮小3D渲染透視圖
恢復:將3D渲染透視圖恢復至默認尺寸。
放大:放大3D渲染透視圖
調整可視視圖範圍:隱藏視圖或展現視圖,一步步解析3D渲染視圖,向左或者向右滑動滑塊兒有相反的效果。
有了這個圖層關係,咱們能夠很清楚的知道頁面上邊的各個控件的位置關係,由於咱們在開發階段�尤爲是測試階段,某個控件上邊的字不顯示,或者控件的字被遮擋,咱們能夠用視圖調試器查看,是否控件是frame設置的不合理。
<2>memory graph
【經驗1】
這個是xcode8新增的功能,翻譯過來的意思就是:內存圖。有了內存圖咱們就能夠解決閉包引用循環問題了。舉個栗子,咱們寫個循環引用,以下:
(說實在的,這幾個命令我在終端不知道怎麼調用,試了半天,仍是沒有搞出來,應該就是內存圖調試的樹狀結構。若是有誰在終端裏面知道怎麼搞出來,煩請告訴我一下具體怎麼操做,謝謝了!)因此,這裏咱們直接看這個memory graph按鈕點擊後的效果。
【經驗2】
說明1:這個功能是xcode8新增的功能,那麼xcode7上邊確定找不到!而xcode8還要10.11.5以上的系統,因此,建議你們先把升級電腦系統,而後安裝xcode8。
說明2: 真機的話還須要iOS9或者10的系統。
說明3:查找當前默認Xcode.app的developer路徑---終端命令行:xcode-select -p。
若是安裝了多個版本的xcode工具,可使用xcode-select命令指定命令行指令使用哪一個版本xcode下的developer目錄下的調試工具,即修改路徑:xcode-select --switch /Applications/Xcode2.app/Contents/Developer。
踩過的坑:
<1>原本我用的是xcode7.2,挺好用,結果在xcode7上邊顯然沒有這個按鈕,升級到xcode8吧。
<2>同事airdrop傳來的xcode8.0和xcode8.1,結果提示安裝不上,須要升級電腦的系統。
<3>升級好了系統,安裝好了同事airdrop傳來的xcode8.0不顯示。(!準備開始抓狂 !)難道須要8.1才行?!
<4>安裝好了同事airdrop傳來的xcode8.1,依舊不顯示。(!!!抓狂!!!)難道是�打開xcode時候的路徑不對?!
<5>修改打開xcode的路徑爲xcode8.1,依舊不顯示。(早已經料到是這個結果了)難道是xcode安裝的太多了?!
<6>卸載掉xcode7.0.一、7.二、7.三、8.0,只剩下一個8.1,依舊不顯示。(淡定的接受這個結果)難道是同事傳的xcode包有問題?!在App Store上本身下載!!
<7>�下載好了,安裝好了,依舊不顯示。(�生無可戀了。。。)
就在此時,我不知道是被逼瘋了仍是靈光一閃,拿了同事的真機運行項目,結果竟然有了memory graph按鈕!!!!
當你認爲最困難的時候,偏偏也就是你最接近成功的時候!
當你放棄的時候,你永遠不會知道你離成功是那麼的接近!
成功很簡單,就是在你堅持不住的時候,再堅持一下!
因此建議使用memory graph功能以前確保:
升級電腦系統並安裝xcode8.1。(8.1比8.0更穩定)(這裏其實並不非得是App Store本身下載,別人用airdrop傳的也行)
真機iOS系統在9.0以上。
【經驗3】
當你點擊這個按鈕之後,xcode上邊的狀態條會顯示下圖:
Capturing memory graph for YourAppName:翻譯過來就是正在捕捉你的app內存圖。
來來來,咱們先來看看這狗東西......不是,是點擊後的效果圖😂😂😂
我想說,這是啥玩意?!
【經驗4】
在解釋上邊圖結構和各個圖標表示的含義以前咱們先來看看這幾個歎號分別表明什麼。
紅色歎號:這個最多見,Error=錯誤。
黃色歎號:這個也常見,Warning=警告。
紫色歎號:這個不常見,不過使用了memory graph咱們就會常常見到,Runtime Issue=運行時問題。
藍色箭頭:這個是檢查內存泄露是見到的,靜態分析Command+Shift+B就能夠看見。Static Analyzer Issue=靜態分析的問題。
紅色叉叉:這個實在instruments裏面Leaked用法的時候見到過。UI Test Failed=UI測試失敗。
在memory graph狀態下咱們點擊左邊的歎號,就會看到RunTime Issue:
【經驗5】
在解決這個紫色歎號以前咱們先來看看右邊灰色三棱錐、藍色正方體、綠色圓圈等等都表明的是什麼。
綠色的通常都是 UIKit 控件及其子類
藍色通常 NSObject 類及其子類
黃色通常都是容器類型及其子類
灰色括號是指 block
固然還有不少一些其餘的類型,具體的你們去看右上角的 Memory Inspect 界面就好,上面都會有詳細的信息。
【經驗6】
當咱們在某一個塊上邊點擊右鍵的時候,會彈出一個選項框,裏面有5個選項:
Quick Look:快速查看,和上圖中的小眼睛功能同樣。
Print Description:打印詳細信息,和上圖小眼睛右邊的按鈕功能同樣。
Jump To Definition:跳至代碼區。
Reveal in Debug Navigator:在左邊的內存樹狀結構中標藍色。
Focus on Node:在節點上關注,點擊後只剩下跟本身先後箭頭相關的節點node。
一開始進入首頁的時候,只展現tabbar上邊的四個controller。而當咱們想要看某個頁面的memory graph的時候,咱們須要在真機上邊跳到那個頁面,而後再點擊memory graph按鈕,才能在左邊的樹狀結構中找到想要看的頁面的controller。
【經驗7】
和紅色、黃色歎號同樣,紫色歎號出來了,咱們就要想辦法解決掉,那麼咱們看看項目中的紫色歎號都標記在哪裏了呢?
各類Reachability啊!!!好不容易找到這個memory graph按鈕,好不容易看明白點了內存圖,好不容易找到了紫色歎號,你踏瑪德告訴我這個錯誤是由於蘋果本身的Reachability形成的!!!(生無可戀。。。。。。。。。。。。。。。。。)紫着吧。
斷點裏面根據做用和功能也有不少種類:普通斷點、條件斷點、異常斷點、符號斷點等。咱們一一學習介紹。
(1)普通斷點(不帶技能就出去闖蕩江湖的)
當程序運行到斷點處時會暫停運行。好比斷點打在11行,那麼程序就會停在11行(注意:程序只運行到了前10行,第11行其實尚未被執行。)。只要在代碼行旁邊點擊,就能添加一個斷點,再次點擊,斷點變成淺藍色,就能讓斷點不可用(disable了,仍然存在,只是不起做用了)。
(2)條件斷點(帶上技能再闖蕩江湖)
打上斷點以後,對斷點進行編輯,設置相應過濾條件。單擊右鍵會彈出選項框:
Edit BreakPoint... :編輯斷點。
Disable BreakPoint :斷點失效。(至關於上邊說到的單擊斷點變成淺藍色,斷點失效)
Delete BreakPoint :刪除斷點。
Reveal in BreakPoint Navigator :在左邊的斷點樹狀結構代表該斷點。
這裏咱們主要用到的是第一個:Edit BreakPoint。這裏面設置斷點的篩選條件(雙擊斷點也能夠快速進入編輯斷點的對話框)。
【1】Condition:返回一個布爾值,當布爾值爲真觸發斷點,通常裏面咱們能夠寫一個表達式。
【2】Ignore: 忽略前N次斷點,到N+1次再觸發斷點。
【3】Action: 斷點觸發事件,分爲六種:
<1> AppleScript:執行腳本。
<2> Capture GPU Frame:用於OpenGL ES調試,捕獲斷點處GPU當前繪製幀。
<3> Debugger Command:和控制檯中輸入LLDB調試命令一致。
<4> Log Message:輸出自定義格式信息至控制檯。
<5> Shell Command:接收命令文件及相應參數列表,Shell Command是異步執行的,只有勾選「Wait until done」纔會等待Shell命令執行完在執行調試。
<6> Sound:斷點觸發時播放聲音。
【4】Options(Automatically continue after evaluating actions選項):選中後,表示斷點不會終止程序的運行。
【1】Condition:這裏我設置i==6,咱們看LLDB控制檯打印結果:
這裏打印了0-5,而後斷點斷了。這樣作的目的就是咱們不用在循環裏面一個一個的點擊下一步,直接跳至咱們想要看到的那一步。
【2】Ignore:這裏我把Condition的條件取消,設置Ignor的條件爲3,咱們看LLDB控制檯打印結果:
結果是將0-2的循環直接忽略,然後邊的循環依舊每次在斷點的位置斷一次。
(3) 異常斷點 Exception Breakpoint(全局斷點)
異常斷點能夠快速定位不知足特定條件的異常,好比常見的數組越界,這時候很難經過異常信息定位到錯誤所在位置。這個時候異常斷點就能夠發揮做用了。
添加異常斷點:
一樣的,全局斷點也是能夠編輯的,單擊右鍵或者雙擊斷點就會彈出編輯框,編輯的項目和上述是同樣的。
(4)符號斷點 Symbolic Breakpoint
符號斷點的建立也同異常斷點。通常符號斷點能夠在你指定的[類名 方法名]時中斷執行。
(1)靜態分析:經過對代碼靜態分析,找出代碼潛在的錯誤,如內存泄漏、空引用、未使用函數等。
方法:菜單「Product"->"Analyze"或者Shift+Command+B,而後想辦法消滅藍箭頭。
(2)動態分析:經過Instruments工具跟蹤分析程序運行時的數據
方法:參考《Instruments性能檢測》
(1)模擬器調試:FPS
在《Instruments性能檢測》一文中咱們就介紹了FPS=Frame Per Sencond:�一秒鐘渲染多少幀。
根據蘋果全球開發者大會WWDC的說法,當FPS低於45時,用戶就會察覺滑動有卡頓。
編譯並運行應用程序,選中模擬器,從 Debug菜單中選擇Color Blended Layers選項。
紅色:表示這些layer是透明的,系統在渲染這些像素點的時候,須要將該view及view一下的其餘view混合以後才能獲得實際的顏色。紅色越深,代表系統在渲染的時候越費勁。
綠色:表示這些layer是不透明的,易於渲染。
黃色:表示這裏的點沒法直接繪製在屏幕上,此時系統須要對相鄰的像素點作反鋸齒計算,增長了圖形負擔。產生的緣由是這個控件的背景是經過圖片拉伸獲得的。
因此推薦儘量地使用不透明的圖層。
(2) 真機調試:截圖。
當咱們在模擬器上邊運行項目的時候,想要給產品或者測試人員看一下頁面效果如何,qq截圖就能夠了,若是在真機上呢怎麼截圖呢?通常咱們會拿着真機給產品或者測試人員看看,可是若是來回折騰很麻煩,咱們也能夠用本身的手機照相而後發圖片給他們,這裏還有一個更好的辦法對真機進行截圖:運行項目,選擇Debug--View Debugging--Take Screenshot of 真機名字,這樣在你的電腦桌面上邊就會有一張你的真機上邊選好頁面的截圖。
參考:一、iOS各類調試技巧豪華套餐
三、Xcode8調試黑科技:Memory Graph實戰解決閉包循環引用問題
五、Xcode8的Debug新特性---WWDC 2016 Session 410 & 412 學習筆記
1、Xcode調試技巧之:NSLog
上面也提到了,在咱們平常的開發過程當中最多見的Debug方式就是打Log。而在OC語言中,打Log是採用NSLog方法。可是NSLog效率低下,具體緣由能夠看這篇博客(《NSLog效率低下的緣由及嘗試lldb斷點打印Log》)。因此在平時的開發過程當中,能不打Log就不打Log。實在想打Log網上也有對NSLog的一些優化方法,能夠閱讀王巍的《宏定義的黑魔法 - 宏菜鳥起飛手冊》以下代碼便出自其中:
1
2
3
4
5
6
7
|
#define NSLog(format, ...) do { \
fprintf(stderr,
" %s\n"
, \
[[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String], \
__LINE__, __func__); \
(NSLog)((format),
##__VA_ARGS__); \
fprintf(stderr,
"-------\n"
); \
}
while
(0)
|
另外在使用NSLog的時候應當注意,release版本中應該要去掉NSLog。
2、Xcode調試技巧之:LLDB
一、po:print object的縮寫,表示顯示對象的文本描述,若是對象不存在則打印nil。
簡單的打印一個對象咱們就不說了,咱們來講說特殊的應用場景吧!
應用場景:你想知道一個視圖包含了哪些子視圖。固然你能夠循環打印子視圖,可是下面只須要一個命令便可解決。
輸出視圖層級關係(這是一個被隱藏的命令):po [[self view] recursiveDescription]
還有個常見的調試場景,好比你要打印一個model。你直接用NSLog或po對象處理的結果是model的地址,這並非咱們想要的。怎麼辦?有沒有解決方法呢?
答案是有的。你能夠重寫model裏面的description方法。可是,若是model裏屬性很是多,這樣就不適用了。你不可能說在description方法裏面拼接屬性返回。這樣不只麻煩,並且可讀性很是差。到這裏,咱們能夠利用runtime動態獲取屬性並返回。不過我並不建議你重寫description方法,我推薦你重寫debugDescription方法(至於詳細的介紹以及如何重寫請點擊此處),由於debugDescription方法和description方法效果同樣,區別在於debugDescription方法是在你使用po命令時調用的,實際上也是調用了description方法。
二、p:能夠用來打印基本數據類型。
三、call:執行一段代碼
1
|
call NSLog(@
"%@"
, @
"yang"
)
|
四、expr:動態執行指定表達式
expr i = 101
輸出:(int)$0 = 101
五、bt:打印當前線程堆棧信息
若是要打印因此線程堆棧信息,使用:bt all便可。
六、image:經常使用來尋找棧地址對應代碼位置:
舉個栗子:
應用場景(數組越界)模擬代碼:
1
2
|
NSArray *array = @[@
"yang"
,@
"she"
,@
"bing"
];
NSLog(@
"%@"
,array[3]);
|
錯誤信息以下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
*** 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
|
這個時候咱們若是懷疑出錯的地址是0x0000000104c25550,那麼咱們可使用下面命令來找出錯誤代碼的位置:
image lookup --address 0x0000000104c25550
執行命令後輸出結果以下:
Address: BGMultimediaDemo[0x0000000100001550] (BGMultimediaDemo.__TEXT.__text + 192)
Summary: BGMultimediaDemo`-[ViewController viewDidLoad] + 192 at ViewController.m:30
從上面輸出結果中能夠看出,錯誤位置應該是ViewController.m文件中的30行。是否是超級好用?反正我以爲好用。
3、Xcode調試技巧之:斷點(Breakpoint)
斷點,程序員Debug必備技之一。
一、條件斷點
打上斷點以後,對斷點進行編輯,設置相應過濾條件。下面簡單的介紹一下條件設置:
Condition:返回一個布爾值,當布爾值爲真觸發斷點,通常裏面咱們能夠寫一個表達式。
Ignore:忽略前N次斷點,到N+1次再觸發斷點。
Action:斷點觸發事件,分爲六種:
AppleScript:執行腳本。
Capture GPU Frame:用於OpenGL ES調試,捕獲斷點處GPU當前繪製幀。
Debugger Command:和控制檯中輸入LLDB調試命令一致。
Log Message:輸出自定義格式信息至控制檯。
Shell Command:接收命令文件及相應參數列表,Shell Command是異步執行的,只有勾選「Wait until done」纔會等待Shell命令執行完在執行調試。
Sound:斷點觸發時播放聲音。
這些功能平時在調試程序的過程當中均可以進行嘗試,說實話我用的設置Condition項會較多些。
Options(Automatically continue after evaluating actions選項):選中後,表示斷點不會終止程序的運行。
二、異常斷點
異常斷點能夠快速定位不知足特定條件的異常,好比常見的數組越界,這時候很難經過異常信息定位到錯誤所在位置。這個時候異常斷點就能夠發揮做用了。
添加異常斷點:
編輯異常斷點:
Exception:能夠選擇拋出異常對象類型:OC或C++。
Break:選擇斷點接收的拋出異常來源是Throw仍是Catch語句。
三、符號斷點
符號斷點的建立方式和異常斷點同樣同樣的,在符號斷點中能夠指定要中斷執行的方法:
舉個例子,常見的場景,我想讓它執行到ViewController類中的viewWillAppear方法就中斷執行:
Symbol:[ViewController viewWillAppear:]即[類名 方法名]能夠執行到指定類的指定方法中開始斷點。若是隻有viewWillAppear:即方法名,它會執行到因此類中的viewWillAppear:方法中開始斷點。
4、Xcode調試技巧之:EXC_BAD_ACCESS
一、開啓殭屍對象
開啓Zombie模式以後會致使內存上升,由於因此已經被釋放(引用計數爲0)的對象被殭屍對象取代,並未真的釋放掉。這個時候再給殭屍對象發送消息,就會拋出異常,並打印出異常信息,你能夠輕鬆的找到錯誤代碼位置,結束Zombies時會釋放。它的主要功能是檢測野指針調用。
使用方法:
「Edit Scheme…」 —> 「Run」 —> 「Diagnostics」 —> 「Zombie Objects」
打開」Edit Scheme…」窗口:
開啓Zombie模式:
注意:Zombie模式不能再真機上使用,只能在模擬器上使用。
二、Address Sanitizer(地址消毒劑)
在Xcode7以後新增了AddressSanitizer工具,爲咱們調試EXC_BAD_ACCESS錯誤提供了便利。當程序建立變量分配一段內存時,將此內存後面的一段內存也凍結住,標識爲中毒內存。程序訪問到中毒內存時(訪問越界),當即中斷程序,拋出異常並打印異常信息。你能夠根據中斷位置及輸出的Log信息來解決錯誤。固然,若是變量已經釋放了,它所佔用的內存也會被標識爲中毒內存,這個時候訪問這片內存空間一樣會拋出異常。
使用方法:
「Edit Scheme…」 —> 「Run」 —> 「Diagnostics」 —> 「Zombie Objects」
開啓AddressSanitizer以後,在調試程序的過程當中,若是有遇到EXC_BAD_ACCESS錯誤,程序則會自動終端,拋出異常。
5、結語
文中提到的這些只是iOS開發過程當中比較常見的一部分Debug方式。其餘的還有好比說:Profile,Analyze分析,View Hierarchy(在調試視圖顯示異常時用的比較多)等,有興趣的能夠自行了解。
本文內容中部分來自網絡,後續會不斷更新完善。歡迎一塊兒學習交流!
6、參考博客地址: