調試地址
【1.普通斷點】
當程序運行到斷點處時會暫停運行。比如斷點打在23行,那么程序就會停在23行(注意:程序只運行到了前22行,第23行其實還沒有被執行!!!)。在某一行創建斷點的快捷鍵是:command+\
并能在調試過程中在下方看到參數的值:
【2.條件斷點】
我們還能對斷點的屬性進行配置,設置條件,使斷點更加智能化,右鍵斷點進入編輯對話框:
我以一個循環作為測試代碼
循環中的代碼每次都要單步執行,可能這并不是我想要的。我想要在i為3的時候中斷程序,進行調試,編寫條件如下:
設置i==3的條件后,程序就會在該條件時中斷,而不會每次到達該位置都中斷。中斷時輸出如下:
同時也可以設置Ignore參數,會忽略前面n次的斷點運行,會在第n+1次中斷。
調試輸出如下:
同時,還可以查看某個函數被調用的次數,設置Action參數如下,注意要選中Automatically continue after evaluating actions.
輸出結果如下:
【3.異常斷點】
開發iOS知道,如果我們因為異常然后程序crash了,代碼就直接跑到main.m
的main函數中去了。為什么就不能跑到出現異常的代碼中呢???異常斷點就為我們解決該問題,程序就會在異常出現的那行代碼終止。創建異常斷點圖例如下:
如下所示就創建完成了。如果碰到異常crash時,嘗試使用異常斷點吧。
【4.符號斷點Symbolic Breakpoint】
符號斷點的創建也同異常斷點。一般符號斷點可以在你指定的[類名 方法名]時中斷執行。
配置符號斷點如下:可以當執行到ViewController類的viewDidLoad方法時中斷執行。
如果你的Symbol只寫了一個函數名,那么就會在出現該函數名的地方就中斷執行。如下,就會在運行到doAnimation的時候中斷。是不是很強大呢?
【5.Analyze分析器】
Analyze分析器是一種靜態的工具,可以對我們的程序進行分析,找出我們未使用的變量,或一些死存儲。執行Analyze如下:Product-->Analyze. 如下藍色的標記就是靜態分析的結果。
1.靜態內存分析概念:不運 程序,直接對代碼進 內存分析,分析是否有內存泄露優點:分析速度快,可以快速對所有的代碼進 內存分析,查找出來對應的內存泄露缺點:不 定準確,但是基本準確.根據語法上下 來分析你的程序到底有沒有內存注意:如果提 有內存泄露, 定要根據上下 語法分析代碼是否有問題.
當然,我們可以設置在編譯程序的時候同時Analyze,把下列選項設為Yes即可。
【6.Profile檢查器Leaks】
這個工具實在是太NB了,三言兩語說不完,貼張圖,大家感受一下,我會在以后的博客中慢慢講解該工具的使用。同樣在Product-->Profile中打開
動態內存分析 概念:真正運 起來程序,并且借助 具來分析代碼是否有某些地 產 了內存泄露 優點:分析 常準確,并且只要分析出來有內存泄露,基本可以斷定代碼 定有問題 缺點:需要 處 處來分析,并不能對全局的代碼進 分析. 注意:在真實開發中,應該是靜態內存分析和動態內存分析結合的 式來分析內存. 特別是ARC環境下 的CoreFoundation框架的東 ,使 靜態內存分析先分析,之 后使 動態內存分析再來分析 次,
【7.僵尸對象】
iOS中把那些已經release但還沒完全消失的對象叫做僵尸對象,對已經release的對象再次釋放,就會發生異常。雖然自從使用ARC后,由于對象釋放產生的異常已經大大變少,但偶爾還會出現。開啟僵尸對象模式后,就能快速定位到異常位置。開啟方式如下:Product-->Scheme-->Edit Scheme. 勾選Enable Zombie Objects即可。
【8.lldb命令】
Xcode中使用llvm編譯器,公認為最好的C、C++、OC、Swift編譯器。而lldb是llvm中的調試器,我們可以使用一些簡單的命令進行調試,我還是把上面的循環代碼作為測試代碼。
斷點調試中,使用po命令、print命令在Console控制臺打印出變量信息:
【9.NSLog打印】
應該說NSLog打印信息是初學者最喜歡的調試手法,也是最簡單的調試,通過打印出的信息查看程序運行的路徑。但是打印出的信息較少,本身NSLog效率較低,有人使用宏做了部分優化,代碼如下:能夠打印出所在類名、所在方法名、詳細時間、行號。
#import "ViewController.h"
#define NSLog(format, ...) do { \
fprintf(stderr, "<%s : %d> %s\n", \
[[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String], \
__LINE__, __func__); \
(NSLog)((format), ##__VA_ARGS__); \
fprintf(stderr, "-------\n"); \
} while (0)
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
for (int i = 0; i < 5; i++) {
NSLog(@"我的值:%d",i);
}
}
@end
打印結果如下:
【10.生命周期方法init,dealloc】
對于ViewController來說,有兩個生命周期函數我們可以進行重寫,也就是init和dealloc方法。對于某些對象的狀態,我們可以在這兩個方法中查看。尤其是在dealloc中可以看到當ViewController退出的時候某個對象是否release。
- (instancetype)init
{
self = [super init];
if (self) {
//初始化語句;
}
return self;
}
- (void)dealloc
{
//釋放后調用;
}
【11.查看代碼運行時間】
有時候我們想要準確的知道某段代碼、某個循環執行的時間,然后分析效率等問題,這個時候就需要執行時間是多少。正好看到網上已經有人做了這個工作,我就直接摘下來了。正好也用了宏的方式計算時間,我們只要在需要計算時間的代碼塊前后寫上TICK,TOCK宏即可。當然,原理也是非常的簡單,也就是使用NSDate計算差值。
#import "ViewController.h"
#define TICK NSDate *startTime = [NSDate date]
#define TOCK NSLog(@"Time: %f", -[startTime timeIntervalSinceNow])
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
TICK;
for (int i = 0; i < 5; i++) {
NSLog(@"我的值:%d",i);
}
TOCK;
}
@end
打印結果如下:
【12.viewDidLoad不建議寫太多代碼】
不要在viewDidLoad方法中寫入太多代碼。尤其是涉及該界面中的動畫的時候,因為執行viewDidLoad方法的時候,界面可能還沒完全加載出來,如果此時把動畫放在viewDidLoad中,可能會造成動畫無法顯示。當然也不建議把耗時的網絡請求和動畫效果都放在viewDidLoad中,界面的阻塞也會造成動畫無法顯示。可以嘗試把動畫放在viewDidAppear,viewWillAppear方法中。對于這類涉及UI的問題,調試也是比較麻煩的。。。
【13.視圖調試】
如今iOS開發的UI設計有很多種方式,比如storyboard,xib,代碼實現。對于stoayboard,xib可視化實現是比較簡單的,但是對于一些“iOS老程序員”而言,都喜歡使用代碼實現UI,并且可能UI層次還比較復雜。這樣就給我們新接手項目的開發者帶來很多困擾。如何快速查看一個復雜UI的界面層次和布局,最快的方法就是用到視圖調試。
當項目運行到某一個界面(可以是模擬器或真機)時,開啟視圖調試,點擊按鈕如圖:
。
這樣就會進入試圖調試,你可以很方便的查看這個界面。這里可以看到控件之間的層次關系。
。
左側的樹形層次圖可以在查看線程、隊列和UI之間切換:
。
。
【14】常用的編譯宏定義:可以讓代碼在不同的編譯情況下執行。
(1)OPTIMIZE :用于release和debug的判斷,當選擇了OPTIMIZE 時,可以讓代碼在release時執行,在debug時不執行。示例如下:
#ifndef __OPTIMIZE__
//這里執行的是debug模式下
else
//這里執行的是release模式下
#endif
(2)i386 與 x86_64 :用于模擬器環境和真機環境的判斷。滿足該條件的代碼只在模擬器下執行。示例代碼如下:
#if defined (__i386__) || defined (__x86_64__)
//模擬器下執行
#else
//真機下執行
#endif
(3)__IPHONE_OS_VERSION_MAX_ALLOWED :當前編譯的SDK版本,可以與__IPHONE_9_0等宏定義進行比較,進行不同版本下代碼的執行。示例如下:
if (__IPHONE_OS_VERSION_MAX_ALLOWED == __IPHONE_9_0) {
//如果當前SDK版本為9.0是執行這里的代碼
}else{
//否則執行這里
}
【15】預編譯宏在開發調試中非常有用,我們來仔細實踐一下:
(1)if的預編譯命令,根據后面的條件判斷是否執行,因為這里條件為1,始終為真,所以在#if...#endif中的代碼一定會執行。
#if 1
//這里的代碼一定會執行
#endif
(2)這里的條件為0,為假,所以#if...#endif里面的代碼一定不會執行。
#if 0
//這里的代碼一定不會執行
#endif
(3)預編譯命令還能根據是否宏定義某個標志,來選擇是否執行。
#ifdef MY_TOP
//如果 MY_TOP 被宏定義過,那么里面的代碼會執行,否則不會執行。
#endif
(4)預編譯命令還可以進行嵌套,就像普通的條件判斷一樣。
#ifdef MY_TOP
//如果 MY_TOP 被宏定義過,那么里面的代碼會執行,否則不會執行。
#if 1
//根據條件判斷是否執行
#endif
#ifdef MY_TOP2
//嵌套判斷
#endif
#endif
(5)既然有#ifdef...#endif,相反的,也有#ifndef...#endif,#ifndef的作用正好和#ifdef相反,如果宏定義沒有被聲明,那么將會執行執行;如果宏定義被聲明了,就不會執行下面的代碼。
#ifndef YOU_TOP
//如果 YOU_TOP 沒有被聲明,就會執行里面的代碼,否則不會執行。
#endif
(6)既然是類似條件判斷,那么就不得不說#else,在條件編譯中,同樣有#if...#else的判斷,執行邏輯和普通的條件判斷一樣。
#ifdef MY_TOP
//如果 MY_TOP 被宏定義過,那么這里的代碼會執行,否則不會執行。
#else
//要么執行else外面的,要么執行else里面的。
#if 1
//根據條件判斷是否執行
#endif
#endif
(7)每出現一次if,就一定要有#endif去包裹,否則預編譯器會提示你錯誤。如果有多層的嵌套,不同層次需要有適當的縮進。
(8)對于項目暫時不執行的代碼,不建議使用注釋代碼去禁用。推薦使用預編譯命令去打開關閉一段代碼。
【16】#warning的使用
有時候在代碼中出現大量黃色的警告是非常煩人的事情,大量的警告不利于代碼的維護與調試。但是有時候我們需要手動去打一些警告,讓我們記住這里應該要注意些什么重要的事情。在code review中,別人也會打一些#warning,提示你應該要注意什么問題,方便我們去查找問題和修改。
。
【17】 charles青花瓷的使用
總結,調試不僅僅是我上面提到的技巧,更多的是長年累月積累下來的經驗,只有在自己的開發中不斷的出錯、試錯、調錯、解決錯誤的過程中才能提高自己的編程水平和調試能力。我會繼續更新該篇博客,講解更多的調試技能,希望我們都能在實踐中提高進步。