1. 程式人生 > >xcode中的除錯技巧

xcode中的除錯技巧

XCode 內建GDB,我們可以在命令列中使用 GDB 命令來除錯我們的程式。下面將介紹一些常用的命令以及除錯技巧。

po 命令:為 print object 的縮寫,顯示物件的文字描述(顯示從物件的 description 訊息獲得的字串資訊)。

比如:


上圖中,我使用 po 命令顯示一個 NSDictionary 的內容。注意在左側我們可以看到 dict 的一些資訊:3 key/value pairs,顯示該 dict 包含的資料量,而展開的資訊顯示 isa 層次體系(即class 和 metaclass結構關係)。我們可以右擊左側的 dict,選中“Print Description of "dict"”,則可以在控制檯輸出 dict 的詳細資訊:

[cpp] view plaincopyprint?

Printing description of dict:  

<CFBasicHash 0x1001149e0 [0x7fff7e27ff40]>{type = immutable dict, count = 3,  

entries =>  

    0 : <CFString 0x100002458 [0x7fff7e27ff40]>{contents = "first"} = <CFString 0x100002438 [0x7fff7e27ff40]>{contents = "one"}  

    1 : <CFString 0x100002498 [0x7fff7e27ff40]>{contents = "second"} = <CFString 0x100002478 [0x7fff7e27ff40]>{contents = "two"}  

    2 : <CFString 0x1000024d8 [0x7fff7e27ff40]>{contents = "third"} = <CFString 0x1000024b8 [0x7fff7e27ff40]>{contents = "three"}  

}  

(gdb)   


print 命令:有點類似於格式化輸出,可以輸出物件的不同資訊:

如:

[cpp] view plaincopyprint?

(gdb) print (char *)[[dict description] cStringUsingEncoding:4]  

$1 = 0x1001159c0 "{\n    first = one;\n    second = two;\n    third = three;\n}"  

(gdb) print (int)[dict retainCount]  

$2 = 1  

(gdb)   

注:4是 NSUTF8StringEncoding 的值。

info 命令:我們可以檢視記憶體地址所在資訊

比如 "info symbol 記憶體地址" 可以獲取記憶體地址所在的 symbol 相關資訊:

[cpp] view plaincopyprint?

(gdb) info symbol 0x00000001000017f7  

main + 343 in section LC_SEGMENT.__TEXT.__text of /Users/LuoZhaohui/Library/Developer/Xcode/DerivedData/RunTimeSystem-anzdlhiwvlbizpfureuvenvmatnp/Build/Products/Debug/RunTimeSystem  

比如 "info line *記憶體地址" 可以獲取記憶體地址所在的程式碼行相關資訊:

[cpp] view plaincopyprint?

(gdb) info line *0x00000001000017f7  

Line 62 of "/Users/LuoZhaohui/Documents/Study/RunTimeSystem/RunTimeSystem/main.m" starts at address 0x1000017f7 <main+343> and ends at 0x10000180a <main+362>.  


show 命令:顯示 GDB 相關的資訊。如:show version 顯示GDB版本資訊

[cpp] view plaincopyprint?

(gdb) show version  

GNU gdb 6.3.50-20050815 (Apple version gdb-1708) (Mon Aug  8 20:32:45 UTC 2011)  

Copyright 2004 Free Software Foundation, Inc.  

GDB is free software, covered by the GNU General Public License, and you are  

welcome to change it and/or distribute copies of it under certain conditions.  

Type "show copying" to see the conditions.  

There is absolutely no warranty for GDB.  Type "show warranty" for details.  

This GDB was configured as "x86_64-apple-darwin".  

help 命令:如果忘記某條命令的語法了,可以使用 help 命令名 來獲取幫助資訊。如:help info 顯示 info 命令的用法。

[cpp] view plaincopyprint?

(gdb) help info  

Generic command for showing things about the program being debugged.  

List of info subcommands:  

info address -- Describe where symbol SYM is stored  

info all-registers -- List of all registers and their contents  

info args -- Argument variables of current stack frame  

info auxv -- Display the inferior's auxiliary vector  

info breakpoints -- Status of user-settable breakpoints  

info catch -- Exceptions that can be caught in the current stack frame  

info checkpoints -- Help  

info classes -- All Objective-C classes  

......  

Type "help info" followed by info subcommand name for full documentation.  

Command name abbreviations are allowed if unambiguous.  

(gdb)   


在系統丟擲異常處設定斷點

有時候我們的程式不知道跑到哪個地方就 crash 了,而 crash 又很難重現。保守的做法是在系統丟擲異常之前設定斷點,具體來說是在 objc_exception_throw處設定斷點。設定步驟為:首先在 XCode 按 CMD + 6,進入斷點管理視窗;然後點選右下方的 +,增加新的 Symbolic Breakpoint,在 Symbol 一欄輸入:objc_exception_throw,然後點選 done,完成。 這樣在 Debug 模式下,如果程式即將丟擲異常,就能在丟擲異常處中斷了。比如在前面的程式碼中,我讓 [firstObjctcrashTest]; 丟擲異常。在 objc_exception_throw 處設定斷點之後,程式就能在該程式碼處中斷了,我們從而知道程式碼在什麼地方出問題了。
 

EXC_Bad_ACCESS除錯

iphone開發的時候EXC_BAD_ACCESS這個bug時不容易找到原因的,在網上找到的3個關於這個問題的方法

首先說一下 EXC_BAD_ACCESS 這個錯誤,可以這麼說,90%的錯誤來源在於對一個已經釋放的物件進行release操作。

1. 重寫object的respondsToSelector方法,現實出現EXEC_BAD_ACCESS前訪問的最後一個object

有時程式崩潰根本不知錯誤發生在什麼地方。比如程式出現EXEC_BAD_ACCESS的時候,雖然大部分情況使用設定 NSZombieEnabled環境變數可以幫助你找到問題的所在,但少數情況下,即使設定了NSZombieEnabled環境變數,還是不知道程式崩 潰在什麼地方。那麼就需要使用下列程式碼進行幫助了:

  1.  #ifdef _FOR_DEBUG_  
  2. -(BOOL) respondsToSelector:(SEL)aSelector {  
  3.     printf("SELECTOR: %s\n", [NSStringFromSelector(aSelector) UTF8String]);  
  4.     return [super respondsToSelector:aSelector];  
  5. }  
  6. #endif  

你需要在每個object的.m或者.mm檔案中加入上面程式碼,並且在 other c flags中加入-D _FOR_DEBUG_(記住請只在Debug Configuration下加入此標記)。這樣當你程式崩潰時,Xcode的console上就會準確地記錄了最後執行的object的方法。

2. 通過NSZombieEnabled

相信很多人都知道通過NSZombies來幫助調試出現EXC_BAD_ACCESS的情況,但有時還是找不到需要的資訊,那麼應該怎麼辦呢?

下面通過一個例子來說明.下面是hello world的程式碼:

  1. NSString* hello = [NSString stringWithFormat:@"Hello world"];  
  2. NSLog(@"What you say is %@",hello);  
  3. [hello release];  

執行後出現EXC_BAD_ACCESS錯誤.但沒有其他任何提示,這時 候通過右擊executables下的應用程式名,選擇get info後,在arguments下輸入環境變數(NSZombieEnabled,MallocStackLogging)

3.設定全域性斷點並快速定位問題程式碼所在行

有兩種方法:一是暴力除錯,二是使用偵錯程式除錯。

什麼是暴力除錯?

暴力除錯就是在你的程式中新增NSLog()語言,將程式的控制流程和一些重要資料輸出到終端上。

偵錯程式除錯:

偵錯程式 就是介於你編寫的應用程式和作業系統之間的一種程式,可以對你的程式進行控制,如停止,執行,單步跟蹤等。

Xcode使用的偵錯程式是GDB。

Xcode除了有GDB外,還有一個微型偵錯程式,它是一個浮動視窗,通過它我們能接跳過GDB偵錯程式做一些簡單的除錯工作。

Xcode有一個提供大量概述資訊的除錯視窗。

Xcode提供一個除錯控制檯,通過它可以直接向偵錯程式發除錯命令。