調(diào)試地址
【1.普通斷點(diǎn)】
當(dāng)程序運(yùn)行到斷點(diǎn)處時(shí)會(huì)暫停運(yùn)行蜕便。比如斷點(diǎn)打在23行,那么程序就會(huì)停在23行(注意:程序只運(yùn)行到了前22行哨坪,第23行其實(shí)還沒(méi)有被執(zhí)行S辜病!5北唷)届慈。在某一行創(chuàng)建斷點(diǎn)的快捷鍵是:command+\
并能在調(diào)試過(guò)程中在下方看到參數(shù)的值:
【2.條件斷點(diǎn)】
我們還能對(duì)斷點(diǎn)的屬性進(jìn)行配置,設(shè)置條件忿偷,使斷點(diǎn)更加智能化金顿,右鍵斷點(diǎn)進(jìn)入編輯對(duì)話框:
我以一個(gè)循環(huán)作為測(cè)試代碼
循環(huán)中的代碼每次都要單步執(zhí)行,可能這并不是我想要的鲤桥。我想要在i為3的時(shí)候中斷程序揍拆,進(jìn)行調(diào)試,編寫條件如下:
設(shè)置i==3的條件后茶凳,程序就會(huì)在該條件時(shí)中斷嫂拴,而不會(huì)每次到達(dá)該位置都中斷。中斷時(shí)輸出如下:
同時(shí)也可以設(shè)置Ignore參數(shù)贮喧,會(huì)忽略前面n次的斷點(diǎn)運(yùn)行筒狠,會(huì)在第n+1次中斷。
調(diào)試輸出如下:
同時(shí)箱沦,還可以查看某個(gè)函數(shù)被調(diào)用的次數(shù)辩恼,設(shè)置Action參數(shù)如下,注意要選中Automatically continue after evaluating actions.
輸出結(jié)果如下:
【3.異常斷點(diǎn)】
開(kāi)發(fā)iOS知道,如果我們因?yàn)楫惓H缓蟪绦騝rash了运挫,代碼就直接跑到main.m
的main函數(shù)中去了状共。為什么就不能跑到出現(xiàn)異常的代碼中呢?谁帕?峡继?異常斷點(diǎn)就為我們解決該問(wèn)題,程序就會(huì)在異常出現(xiàn)的那行代碼終止匈挖。創(chuàng)建異常斷點(diǎn)圖例如下:
如下所示就創(chuàng)建完成了碾牌。如果碰到異常crash時(shí),嘗試使用異常斷點(diǎn)吧儡循。
【4.符號(hào)斷點(diǎn)Symbolic Breakpoint】
符號(hào)斷點(diǎn)的創(chuàng)建也同異常斷點(diǎn)舶吗。一般符號(hào)斷點(diǎn)可以在你指定的[類名 方法名]時(shí)中斷執(zhí)行。
配置符號(hào)斷點(diǎn)如下:可以當(dāng)執(zhí)行到ViewController類的viewDidLoad方法時(shí)中斷執(zhí)行择膝。
如果你的Symbol只寫了一個(gè)函數(shù)名誓琼,那么就會(huì)在出現(xiàn)該函數(shù)名的地方就中斷執(zhí)行。如下肴捉,就會(huì)在運(yùn)行到doAnimation的時(shí)候中斷腹侣。是不是很強(qiáng)大呢?
【5.Analyze分析器】
Analyze分析器是一種靜態(tài)的工具齿穗,可以對(duì)我們的程序進(jìn)行分析傲隶,找出我們未使用的變量,或一些死存儲(chǔ)窃页。執(zhí)行Analyze如下:Product-->Analyze. 如下藍(lán)色的標(biāo)記就是靜態(tài)分析的結(jié)果跺株。
1.靜態(tài)內(nèi)存分析概念:不運(yùn) 程序,直接對(duì)代碼進(jìn) 內(nèi)存分析,分析是否有內(nèi)存泄露優(yōu)點(diǎn):分析速度快,可以快速對(duì)所有的代碼進(jìn) 內(nèi)存分析,查找出來(lái)對(duì)應(yīng)的內(nèi)存泄露缺點(diǎn):不 定準(zhǔn)確,但是基本準(zhǔn)確.根據(jù)語(yǔ)法上下 來(lái)分析你的程序到底有沒(méi)有內(nèi)存注意:如果提 有內(nèi)存泄露, 定要根據(jù)上下 語(yǔ)法分析代碼是否有問(wèn)題.
當(dāng)然,我們可以設(shè)置在編譯程序的時(shí)候同時(shí)Analyze脖卖,把下列選項(xiàng)設(shè)為Yes即可乒省。
【6.Profile檢查器Leaks】
這個(gè)工具實(shí)在是太NB了,三言兩語(yǔ)說(shuō)不完畦木,貼張圖作儿,大家感受一下,我會(huì)在以后的博客中慢慢講解該工具的使用馋劈。同樣在Product-->Profile中打開(kāi)
動(dòng)態(tài)內(nèi)存分析 概念:真正運(yùn) 起來(lái)程序,并且借助 具來(lái)分析代碼是否有某些地 產(chǎn) 了內(nèi)存泄露 優(yōu)點(diǎn):分析 常準(zhǔn)確,并且只要分析出來(lái)有內(nèi)存泄露,基本可以斷定代碼 定有問(wèn)題 缺點(diǎn):需要 處 處來(lái)分析,并不能對(duì)全局的代碼進(jìn) 分析. 注意:在真實(shí)開(kāi)發(fā)中,應(yīng)該是靜態(tài)內(nèi)存分析和動(dòng)態(tài)內(nèi)存分析結(jié)合的 式來(lái)分析內(nèi)存. 特別是ARC環(huán)境下 的CoreFoundation框架的東 ,使 靜態(tài)內(nèi)存分析先分析,之 后使 動(dòng)態(tài)內(nèi)存分析再來(lái)分析 次,
【7.僵尸對(duì)象】
iOS中把那些已經(jīng)release但還沒(méi)完全消失的對(duì)象叫做僵尸對(duì)象攻锰,對(duì)已經(jīng)release的對(duì)象再次釋放,就會(huì)發(fā)生異常妓雾。雖然自從使用ARC后娶吞,由于對(duì)象釋放產(chǎn)生的異常已經(jīng)大大變少,但偶爾還會(huì)出現(xiàn)械姻。開(kāi)啟僵尸對(duì)象模式后妒蛇,就能快速定位到異常位置。開(kāi)啟方式如下:Product-->Scheme-->Edit Scheme. 勾選Enable Zombie Objects即可。
【8.lldb命令】
Xcode中使用llvm編譯器绣夺,公認(rèn)為最好的C吏奸、C++、OC陶耍、Swift編譯器奋蔚。而lldb是llvm中的調(diào)試器,我們可以使用一些簡(jiǎn)單的命令進(jìn)行調(diào)試烈钞,我還是把上面的循環(huán)代碼作為測(cè)試代碼泊碑。
斷點(diǎn)調(diào)試中,使用po命令毯欣、print命令在Console控制臺(tái)打印出變量信息:
【9.NSLog打印】
應(yīng)該說(shuō)NSLog打印信息是初學(xué)者最喜歡的調(diào)試手法馒过,也是最簡(jiǎn)單的調(diào)試,通過(guò)打印出的信息查看程序運(yùn)行的路徑酗钞。但是打印出的信息較少腹忽,本身NSLog效率較低,有人使用宏做了部分優(yōu)化砚作,代碼如下:能夠打印出所在類名留凭、所在方法名、詳細(xì)時(shí)間偎巢、行號(hào)。
#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
打印結(jié)果如下:
【10.生命周期方法init,dealloc】
對(duì)于ViewController來(lái)說(shuō)兼耀,有兩個(gè)生命周期函數(shù)我們可以進(jìn)行重寫压昼,也就是init和dealloc方法。對(duì)于某些對(duì)象的狀態(tài)瘤运,我們可以在這兩個(gè)方法中查看窍霞。尤其是在dealloc中可以看到當(dāng)ViewController退出的時(shí)候某個(gè)對(duì)象是否release。
- (instancetype)init
{
self = [super init];
if (self) {
//初始化語(yǔ)句拯坟;
}
return self;
}
- (void)dealloc
{
//釋放后調(diào)用但金;
}
【11.查看代碼運(yùn)行時(shí)間】
有時(shí)候我們想要準(zhǔn)確的知道某段代碼、某個(gè)循環(huán)執(zhí)行的時(shí)間郁季,然后分析效率等問(wèn)題冷溃,這個(gè)時(shí)候就需要執(zhí)行時(shí)間是多少。正好看到網(wǎng)上已經(jīng)有人做了這個(gè)工作梦裂,我就直接摘下來(lái)了似枕。正好也用了宏的方式計(jì)算時(shí)間,我們只要在需要計(jì)算時(shí)間的代碼塊前后寫上TICK,TOCK宏即可年柠。當(dāng)然凿歼,原理也是非常的簡(jiǎn)單,也就是使用NSDate計(jì)算差值。
#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
打印結(jié)果如下:
【12.viewDidLoad不建議寫太多代碼】
不要在viewDidLoad方法中寫入太多代碼答憔。尤其是涉及該界面中的動(dòng)畫的時(shí)候味赃,因?yàn)閳?zhí)行viewDidLoad方法的時(shí)候,界面可能還沒(méi)完全加載出來(lái)虐拓,如果此時(shí)把動(dòng)畫放在viewDidLoad中心俗,可能會(huì)造成動(dòng)畫無(wú)法顯示。當(dāng)然也不建議把耗時(shí)的網(wǎng)絡(luò)請(qǐng)求和動(dòng)畫效果都放在viewDidLoad中侯嘀,界面的阻塞也會(huì)造成動(dòng)畫無(wú)法顯示另凌。可以嘗試把動(dòng)畫放在viewDidAppear,viewWillAppear方法中戒幔。對(duì)于這類涉及UI的問(wèn)題吠谢,調(diào)試也是比較麻煩的。诗茎。工坊。
【13.視圖調(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)系弓坞。
隧甚。
左側(cè)的樹形層次圖可以在查看線程、隊(duì)列和UI之間切換:
渡冻。
呻逆。
【14】常用的編譯宏定義:可以讓代碼在不同的編譯情況下執(zhí)行。
(1)OPTIMIZE :用于release和debug的判斷菩帝,當(dāng)選擇了OPTIMIZE 時(shí)咖城,可以讓代碼在release時(shí)執(zhí)行茬腿,在debug時(shí)不執(zhí)行。示例如下:
#ifndef __OPTIMIZE__
//這里執(zhí)行的是debug模式下
else
//這里執(zhí)行的是release模式下
#endif
(2)i386 與 x86_64 :用于模擬器環(huán)境和真機(jī)環(huán)境的判斷宜雀。滿足該條件的代碼只在模擬器下執(zhí)行切平。示例代碼如下:
#if defined (__i386__) || defined (__x86_64__)
//模擬器下執(zhí)行
#else
//真機(jī)下執(zhí)行
#endif
(3)__IPHONE_OS_VERSION_MAX_ALLOWED :當(dāng)前編譯的SDK版本,可以與__IPHONE_9_0等宏定義進(jìn)行比較辐董,進(jìn)行不同版本下代碼的執(zhí)行悴品。示例如下:
if (__IPHONE_OS_VERSION_MAX_ALLOWED == __IPHONE_9_0) {
//如果當(dāng)前SDK版本為9.0是執(zhí)行這里的代碼
}else{
//否則執(zhí)行這里
}
【15】預(yù)編譯宏在開(kāi)發(fā)調(diào)試中非常有用,我們來(lái)仔細(xì)實(shí)踐一下:
(1)if的預(yù)編譯命令简烘,根據(jù)后面的條件判斷是否執(zhí)行苔严,因?yàn)檫@里條件為1,始終為真孤澎,所以在#if...#endif中的代碼一定會(huì)執(zhí)行届氢。
#if 1
//這里的代碼一定會(huì)執(zhí)行
#endif
(2)這里的條件為0,為假覆旭,所以#if...#endif里面的代碼一定不會(huì)執(zhí)行退子。
#if 0
//這里的代碼一定不會(huì)執(zhí)行
#endif
(3)預(yù)編譯命令還能根據(jù)是否宏定義某個(gè)標(biāo)志,來(lái)選擇是否執(zhí)行型将。
#ifdef MY_TOP
//如果 MY_TOP 被宏定義過(guò)寂祥,那么里面的代碼會(huì)執(zhí)行,否則不會(huì)執(zhí)行七兜。
#endif
(4)預(yù)編譯命令還可以進(jìn)行嵌套丸凭,就像普通的條件判斷一樣。
#ifdef MY_TOP
//如果 MY_TOP 被宏定義過(guò)腕铸,那么里面的代碼會(huì)執(zhí)行惜犀,否則不會(huì)執(zhí)行。
#if 1
//根據(jù)條件判斷是否執(zhí)行
#endif
#ifdef MY_TOP2
//嵌套判斷
#endif
#endif
(5)既然有#ifdef...#endif,相反的恬惯,也有#ifndef...#endif,#ifndef的作用正好和#ifdef相反,如果宏定義沒(méi)有被聲明亚茬,那么將會(huì)執(zhí)行執(zhí)行酪耳;如果宏定義被聲明了,就不會(huì)執(zhí)行下面的代碼刹缝。
#ifndef YOU_TOP
//如果 YOU_TOP 沒(méi)有被聲明碗暗,就會(huì)執(zhí)行里面的代碼,否則不會(huì)執(zhí)行梢夯。
#endif
(6)既然是類似條件判斷言疗,那么就不得不說(shuō)#else,在條件編譯中颂砸,同樣有#if...#else的判斷噪奄,執(zhí)行邏輯和普通的條件判斷一樣死姚。
#ifdef MY_TOP
//如果 MY_TOP 被宏定義過(guò),那么這里的代碼會(huì)執(zhí)行勤篮,否則不會(huì)執(zhí)行都毒。
#else
//要么執(zhí)行else外面的,要么執(zhí)行else里面的碰缔。
#if 1
//根據(jù)條件判斷是否執(zhí)行
#endif
#endif
(7)每出現(xiàn)一次if,就一定要有#endif去包裹账劲,否則預(yù)編譯器會(huì)提示你錯(cuò)誤。如果有多層的嵌套金抡,不同層次需要有適當(dāng)?shù)目s進(jìn)瀑焦。
(8)對(duì)于項(xiàng)目暫時(shí)不執(zhí)行的代碼,不建議使用注釋代碼去禁用梗肝。推薦使用預(yù)編譯命令去打開(kāi)關(guān)閉一段代碼榛瓮。
【16】#warning的使用
有時(shí)候在代碼中出現(xiàn)大量黃色的警告是非常煩人的事情,大量的警告不利于代碼的維護(hù)與調(diào)試统捶。但是有時(shí)候我們需要手動(dòng)去打一些警告榆芦,讓我們記住這里應(yīng)該要注意些什么重要的事情。在code review中喘鸟,別人也會(huì)打一些#warning,提示你應(yīng)該要注意什么問(wèn)題匆绣,方便我們?nèi)ゲ檎覇?wèn)題和修改。
什黑。
【17】 charles青花瓷的使用
總結(jié)崎淳,調(diào)試不僅僅是我上面提到的技巧,更多的是長(zhǎng)年累月積累下來(lái)的經(jīng)驗(yàn)愕把,只有在自己的開(kāi)發(fā)中不斷的出錯(cuò)拣凹、試錯(cuò)、調(diào)錯(cuò)恨豁、解決錯(cuò)誤的過(guò)程中才能提高自己的編程水平和調(diào)試能力嚣镜。我會(huì)繼續(xù)更新該篇博客,講解更多的調(diào)試技能橘蜜,希望我們都能在實(shí)踐中提高進(jìn)步菊匿。