天天看點

iOS開發調試技巧總結(持續更新中)

       對于軟體開發而言,調試是必須學會的技能,重要性不言而喻。對于調試的技能,基本上是可以遷移的,也就是說你以前在其他平台上掌握的很多調試技巧,很多也是可以用在iOS開發中。不同語言、不同IDE、不同平台的調試,有同性也有個性。今天我們就來學習一下iOS開發中的調試技巧,語言暫用為OC,IDE當然是強大的Xcode。首先說明下,Xcode已經為我們調試項目提供了極大的友善。

【1.普通斷點】

斷點(Breakpoint)絕對是調試程式的第一大選擇,也是掌握的基礎技能。顧名思義,當程式運作到斷點處時會暫停運作。比如斷點打在11行,那麼程式就會停在11行(注意:程式隻運作到了前10行,第11行其實還沒有被執行!!!)。隻要在代碼行旁邊點選,就能添加一個斷點,再次點選,就能讓斷點不可用(disable了,仍然存在,隻是不起作用了)。在某一行建立斷點的快捷鍵是:command+\

iOS開發調試技巧總結(持續更新中)

iOS開發調試技巧總結(持續更新中)

并能在調試過程中在下方看到參數的值:

iOS開發調試技巧總結(持續更新中)

【2.條件斷點】

以上的斷點隻是最普通的,我們還能對斷點的屬性進行配置,設定條件,使斷點更加智能化,右鍵斷點進入編輯對話框:

iOS開發調試技巧總結(持續更新中)

我以一個循環作為測試代碼:

iOS開發調試技巧總結(持續更新中)

循環中的代碼每次都要單步執行,可能這并不是我想要的。我想要在i為3的時候中斷程式,進行調試,編寫條件如下:

iOS開發調試技巧總結(持續更新中)

設定i==3的條件後,程式就會在該條件時中斷,而不會每次到達該位置都中斷。中斷時輸出如下:

iOS開發調試技巧總結(持續更新中)

同時也可以設定Ignore參數,會忽略前面n次的斷點運作,會在第n+1次中斷。

iOS開發調試技巧總結(持續更新中)

調試輸出如下:

iOS開發調試技巧總結(持續更新中)

同時,還可以檢視某個函數被調用的次數,設定Action參數如下,注意要選中Automatically continue after evaluating actions.

iOS開發調試技巧總結(持續更新中)

.

輸出結果如下:

iOS開發調試技巧總結(持續更新中)

【3.異常斷點】

斷點的功能不限于上面所述。開發iOS知道,如果我們因為異常然後程式crash了,代碼就直接跑到main.m的main函數中去了。為什麼就不能跑到出現異常的代碼中呢???異常斷點就為我們解決該問題,程式就會在異常出現的那行代碼終止。建立異常斷點圖例如下:

iOS開發調試技巧總結(持續更新中)

如下所示就建立完成了。如果碰到異常crash時,嘗試使用異常斷點吧。

iOS開發調試技巧總結(持續更新中)

【4.符号斷點Symbolic Breakpoint】

符号斷點的建立也同異常斷點。一般符号斷點可以在你指定的[類名 方法名]時中斷執行。

iOS開發調試技巧總結(持續更新中)

配置符号斷點如下:可以當執行到ViewController類的viewDidLoad方法時中斷執行。

iOS開發調試技巧總結(持續更新中)

如果你的Symbol隻寫了一個函數名,那麼就會在出現該函數名的地方就中斷執行。如下,就會在運作到doAnimation的時候中斷。是不是很強大呢?

iOS開發調試技巧總結(持續更新中)

.

【5.Analyze分析器】

Analyze分析器是一種靜态的工具,可以對我們的程式進行分析,找出我們未使用的變量,或一些死存儲。執行Analyze如下:Product-->Analyze. 如下藍色的标記就是靜态分析的結果。

iOS開發調試技巧總結(持續更新中)

iOS開發調試技巧總結(持續更新中)

當然,我們可以設定在編譯程式的時候同時Analyze,把下列選項設為Yes即可。

iOS開發調試技巧總結(持續更新中)

【6.Profile檢查器】

這個工具實在是太NB了,三言兩語說不完,貼張圖,大家感受一下,我會在以後的部落格中慢慢講解該工具的使用。同樣在Product-->Profile中打開。

iOS開發調試技巧總結(持續更新中)

【7.僵屍對象】

iOS中把那些已經release但還沒完全消失的對象叫做僵屍對象,對已經release的對象再次釋放,就會發生異常。雖然自從使用ARC後,由于對象釋放産生的異常已經大大變少,但偶爾還會出現。開啟僵屍對象模式後,就能快速定位到異常位置。開啟方式如下:Product-->Scheme-->Edit Scheme. 勾選Enable Zombie Objects即可。

iOS開發調試技巧總結(持續更新中)

【8.lldb指令】

Xcode中使用llvm編譯器,公認為最好的C、C++、OC、Swift編譯器。而lldb是llvm中的調試器,我們可以使用一些簡單的指令進行調試,我還是把上面的循環代碼作為測試代碼。

iOS開發調試技巧總結(持續更新中)

斷點調試中,使用po指令、print指令在Console控制台列印出變量資訊:

iOS開發調試技巧總結(持續更新中)

【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
           

列印結果如下:

iOS開發調試技巧總結(持續更新中)

【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
           

列印結果如下:

iOS開發調試技巧總結(持續更新中)

【12.手機截屏】

手機截屏也算調試?哈哈。其實也算是開發中的一個小技巧哈。其實大家都會在iPhone上同時按電源鍵+Home鍵截屏,然後使用各種通訊軟體發給其他人,這個略顯不友善。我們來使用Xcode中的方式截屏。當手機接上電腦後,注意要把調試裝置選為自己的手機:

iOS開發調試技巧總結(持續更新中)

然後選擇Debug-->View Debugging-->Take Screenshot...   .然後可以看到手機螢幕已經在你的電腦桌面了。是不是比直接在手機上操作友善多了呢?這樣就可以快速的發給其他開發者、PM等人了。

【13.viewDidLoad不建議寫太多代碼】

       個人建議不要在viewDidLoad方法中寫入太多代碼。尤其是涉及該界面中的動畫的時候,因為執行viewDidLoad方法的時候,界面可能還沒完全加載出來,如果此時把動畫放在viewDidLoad中,可能會造成動畫無法顯示。當然也不建議把耗時的網絡請求和動畫效果都放在viewDidLoad中,界面的阻塞也會造成動畫無法顯示。可以嘗試把動畫放在viewDidAppear,viewWillAppear方法中。對于這類涉及UI的問題,調試也是比較麻煩的。。。

【14.視圖調試】

       如今iOS開發的UI設計有很多種方式,比如storyboard,xib,代碼實作。對于stoayboard,xib可視化實作是比較簡單的,但是對于一些“iOS老程式員”而言,都喜歡使用代碼實作UI,并且可能UI層次還比較複雜。這樣就給我們新接手項目的開發者帶來很多困擾。如何快速檢視一個複雜UI的界面層次和布局,最快的方法就是用到視圖調試。

       當項目運作到某一個界面(可以是模拟器或真機)時,開啟視圖調試,點選按鈕如圖:

iOS開發調試技巧總結(持續更新中)

這樣就會進入試圖調試,你可以很友善的檢視這個界面。這裡可以看到控件之間的層次關系。

iOS開發調試技巧總結(持續更新中)

左側的樹形層次圖可以在檢視線程、隊列和UI之間切換:

iOS開發調試技巧總結(持續更新中)

iOS開發調試技巧總結(持續更新中)

【15】常用的編譯宏定義:可以讓代碼在不同的編譯情況下執行。

(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{
        //否則執行這裡
    }
           

【16】預編譯宏在開發調試中非常有用,我們來仔細實踐一下:

(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)對于項目暫時不執行的代碼,不建議使用注釋代碼去禁用。推薦使用預編譯指令去打開關閉一段代碼。

【17】#warning的使用

有時候在代碼中出現大量黃色的警告是非常煩人的事情,大量的警告不利于代碼的維護與調試。但是有時候我們需要手動去打一些警告,讓我們記住這裡應該要注意些什麼重要的事情。在code review中,别人也會打一些#warning,提示你應該要注意什麼問題,友善我們去查找問題和修改。

iOS開發調試技巧總結(持續更新中)

       總結,調試不僅僅是我上面提到的技巧,更多的是長年累月積累下來的經驗,隻有在自己的開發中不斷的出錯、試錯、調錯、解決錯誤的過程中才能提高自己的程式設計水準和調試能力。我會繼續更新該篇部落格,講解更多的調試技能,希望我們都能在實踐中提高進步。