iOS故障排除指南:基本技巧
譯文【2013年1月16日 51CTO外電頭條】本文為《iOS故障排除指南:基本技巧》。在iOS程序的數組中的三個對象莫名其妙成了五個、新開發成員的加入令游戲性不升反降:熟悉應用開發的各位朋友對這些情況一定也不陌生,這時調試就成了撥亂反正、收拾舊山河的必需手段。通過閱讀本文,相信大家會對當前最重要的調試方案以及如何利用最短時間搞定問題擁有更加深刻的理解。
教程信息
- 完成時間: 30 分鐘
- 執行難度: 中等
- 相關技術: iOS SDK
在本文中,我們的議題包括以下三點:
- 利用控制臺檢查應用程序狀態;
- 進行日志記錄并深入掌握NSLog操作方法;
- 通過對象生命周期追蹤內存使用情況。
利用控制臺進行檢查
位于Xcode底端的這位仁兄稱得上我們調試工作中的***助手。輸出日志信息、錯誤信息以及其它各類實用信息都是它的拿手絕活,這有助于我們一步步鎖定應用錯誤。除了直接閱讀來自日志記錄的輸出結果,大家還可以在流程中的任意環節暫停,進而檢查應用程序的各個部分。
條件斷點
首先我假設大家已經了解存點的工作機制(如果還不了解也別擔心,看完這幾段內容您也應該基本掌握這部分知識了)。斷點的作用非常重要,它能夠幫我們查看應用程序在給定時間點上的所在位置——但在對象達到特定值并觸發斷點之后,再通過單步調試循環或者遞歸函數的做法實在是個痛苦的過程。這里,我們推薦大家采用條件斷點。
條件斷點,顧名思義是指只會在特定條件下觸發的斷點。設想一下,當我們只希望斷點在某對象在特定狀態或者“第n次”循環更替時發生,條件斷點就成為大家的不二選擇。在Xcode編輯器中點擊“gutter”來在代碼中添加一個斷點 ,右擊該斷點并選擇“edit breakpoint(編輯斷點)”為其設置特殊條件。
大家可以設置條件(例如i==12)或者設定循環中斷點被忽略的次數。當然,我們也可以采取其它一些自動觸發機制,例如在調試命令輸出值時啟用斷點。
提示: 利用快捷鍵組合coomand+\能夠快速添加或刪除斷點。 |
另一項值得關注的斷點技巧是添加“exception breakpoint(異常斷點)”。不知道大家有沒有注意到,當我們遇到異常情況時,Xcode會有99%的機率將我們引向主方法中的自動釋放池。
感謝Xocde……你可真會幫倒忙。
通過設定異常斷點,我們就能夠精確定位引發異常情況的代碼行。要做到這一點,首先得打開異常斷點選項卡(快捷鍵為command+6)。在窗口底部會出現一個“+”按鈕,點選該按鈕即可添加“異常斷點”。現在,一旦Xcode在運行中遭遇異常,即會在引發問題的代碼處中斷。
在控制臺端實現手動輸出
如果我們在應用的特定點進行中斷,一般來說是為了檢查對象的當前狀態。Xcode為我們提供了一套“variables view(變量視圖)”,該視圖位于Xcode底部、與控制臺相鄰。理論上講它的作用是顯示與當前環境相關的所有值的實時狀態。但在實踐中,這一作用有時無法正常生效。或者是無法列出值,或者是并有將值更新為中斷時的***狀態,總之問題不少。
幸運的是,我們可以利用一些非常實用的控制臺命令自己進行對象檢查工作。在控制臺中輸入“po”來獲取特定對象的當前細節信息(我們使用‘p’來處理純量值)。
這種方式在檢查某對象是否已經存在(如果對象不存在則輸出結果為nil)、確定對象值、查詢某數組/字典在當前運行狀態下的內容以及對兩個對象進行比較等方面效果拔群。由于這條命令會輸出相關對象的內存地址,因此我們可以對本應相同的兩個對象進行輸出,查看二者是否正確擁有相同的內存地址。
另一條實用但卻常常被忽視的命令則是recursiveDescription,我們可以用它輕松檢查視圖。運行該命令后,系統會將視圖結構作為結果輸出。
有效日志記錄
在調試程序的過程中,大家常常希望能將特定消息記錄到控制臺中。而“NSLog”函數允許用戶將任何想要的結果輸出至控制臺。對于希望以特定途徑梳理應用程序或者無法根據特定情況一一設定斷點卻仍然想要進行值比較的用戶而言,它的作用相當重要。NSLog的使用格式與[NSString StringWithFormat]相同(如下圖所示)。
提示: 大家可以訪問https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Strings/Articles/FormatStrings.html了解更多Objictive-C中的格式化字符知識。 |
聰明地使用NSLog
盡管NSLog自身功能已經可圈可點,但我們還得在實際應用中再動動腦子。任何由NSLog輸出的內容都會成為成品代碼的一部分,也就是說會被任何接觸到應用的人看到。只要把設備接入信息管理工具,每個人都能查看控制臺信息并查詢每一條日志記錄。沒錯,這必然會引發一系列嚴重后果。試想一下,如果我們曾經向控制臺輸出過機密邏輯算法或者者用戶密碼,這些信息一旦被他人獲得將帶來惡劣影響。有鑒于此,蘋果公司會在App Store的審核流程中檢測控制臺信息。一旦發現其中包含太多輸出結果,應用程序將被直接打回開發者處進行調整。
幸運的是,我們還有更好的辦法實現日志記錄工作。根據大家精力與時間的富裕程度,我們擁有幾種不同的處理方案。最簡單的辦法是做一個宏,其中只包含調試版本中的NSLog。如果時間充實,也可以采用全局可訪問的頭文件;我們可以把所有日志記錄都灌進去,而且不用擔心它們會出現在成品代碼當中(前提是我們沒有對預設的默認宏值做出改動——看不懂也沒關系,直接用就好)。
- #ifdef DEBUG
- #define DMLog(...) NSLog(@"%s %@", __PRETTY_FUNCTION__, [NSString stringWithFormat:__VA_ARGS__])
- #else
- #define DMLog(...) do { } while (0)
現在如果我們使用DMLog(這個名稱可以隨便起),它將只向調試版本輸出結果,任何成品代碼都不會受到影響。_PRETTY_FUNCTION_也幫上了大忙,它會根據日志信息來源為函數命名。
采取下一個步驟
雖然NSLog表現卓越,但其局限性也不可忽視:
- 它只能向本地輸出結果;
- 我們無法為其添加日志“等級”(例如嚴重、警告等等);
- NSLog速度很慢。在進行大量處理時它會嚴重拖慢應用程序的運行效果。
對于想給自己來點挑戰的開發者來說,還有兩款框架能夠回避NSLog的短板——但前輩是大家得有足夠的時間和精力投入其中。下面來看這兩款個人推薦:
- Cocoa LumberJack –一款專為Cocoa打造的知名通用型日志框架,學習起來略困難但功能非常強大。
- SNLog –NSLog的替換方案之一。
盡管自動引用計數(簡稱ARC)的出現令內存管理工作不再成為時間殺手,但在對象的生命周期中追蹤重要事件仍然非常關鍵。畢竟ARC并沒能消除內存泄漏或者嘗試訪問已釋放對象的可能性(它的介入反而使二者的控制工作更加艱難)。為此,我們可以通過一系列流程及工具實現對象當前狀態的監控工作。
記錄重要事件
在Objective-C的對象生命周期中,最重要的兩個方法分別是init與dealloc。將二者的事件記錄納入控制臺非常關鍵,這樣我們就可以從啟用時開始全程監控對象,并確保其在必要時正確結束。
- - (id)init
- {
- self = [super init];
- if (self)
- {
- NSLog(@"%@: %@", NSStringFromSelector(_cmd), self);
- }
- return self;
- }
- - (void)dealloc
- {
- NSLog(@"%@: %@", NSStringFromSelector(_cmd), self);
- }
雖然輸入上述代碼的過程令人煩躁,但我們有辦法利用自動化機制使這項工作變得更輕松。我敢保證當大家的應用程序運行出現異常時,它將起到至關重要的提示作用。大家還可以在其中使用日志片段方面的小技巧,這樣結果就不會被輸出到成品代碼當中(甚至為我們創建一套宏)。
靜分析器與檢查器
Xcode中提供的兩款工具能夠成為我們清理代碼、降低代碼出錯機率的好幫手。靜態分析器(Static Analyzer)工具會揪出釋放對象中那些未被實際使用的對象(ARC在Core Foundation對象方面無法實現這一功能),進而達到改善代碼的目標。要讓它伸出援手,只要在Product中選擇“Anlayze”即可。
檢查器(Inspector)同樣是一款強大的工具,允許我們密切檢測應用程序各方面的內存使用、文件系統活動等情況;它甚至還能自動模擬UI交互操作。要讓它伸出援手,我們只需在Product下拉菜單中選擇“Profile”選項即可。
點選之后會打開一個工具窗口,我們可以從中選擇一套配置模板加以運行。最常見的選擇是zombies(僵尸,這一點我們稍后再詳述)、activity monitor(活動監控)以及leaks(泄露)三種。泄露方案的作用非常直觀——幫助我們追蹤應用程序運行中可能出現的任何內存泄露情況。
“僵尸”也是好朋友
雖然在ARC坐鎮的情況下可怕的EXC_BAD_ACCESS錯誤的出現機率已經大大降低,但在某些極端情況下它仍然有發生的可能。在處理UIPopoverController或者核心基礎對象時,我們仍然能夠對過度釋放的對象發起訪問。通常情況下,一旦內存中的對象被釋放,其只能就此消失。但當“僵尸”方案啟動時,該對象只會被標記為“釋放”但卻仍然被保留在內存當中。如此一來,當我們訪問某個僵尸對象時,Xcode會提醒我們該對象雖然能夠被訪問、但在實際環境中已經不應存在。在這種模式下,我們將能夠了解到正常情況下無法獲得的實時狀態與對象位置。
我們可以通過兩種方法啟用僵尸對象,即在檢查器中運行“僵尸”配置模板還有在“Run”選項中選擇僵尸診斷方案。點擊停止按鈕旁邊的方案名稱,然后點選“Edit Scheme(編輯方案)”、單擊診斷選項卡并選擇“Enable Zombie Objects(啟用僵尸對象)”。請注意,僵尸模式下的調試工作只能在模擬器中實現,我們無法在物理設備上完成這一診斷流程。
綜述
希望本文能夠幫助大家在應用程序調試工作方面拓展思路,我們的目的是盡可能縮短花費在bug修復身上的時間,并把節省下來的精力與資源投入真正重要的應用開發工作中。
當然,我們無法列出所有值得關注的技術或工具。還有很多其它方案需要留意,例如產品調試、遠程bug報告、崩潰報告等等。大家在工作中有哪些心得想與我們分享?對于以上內容又有哪些疑問?請在評論欄中與我們交流。
祝各位編程愉快!
原文鏈接:http://mobile.tutsplus.com/tutorials/iphone/debugging-in-ios-essential-tips/
原文標題:Debugging in iOS – Essential Tips