原文地址: http://www.reibang.com/p/e30caeba3dc0
開發(fā)iOS的時候常常會用到調(diào)試跟蹤擂橘,如何正確的使用調(diào)試器來debug十分重要吃谣。Xcode里有內(nèi)置的Debugger,老版使用的是GDB叼风,Xcode自4.3之后默認使用的就是LLDB了麸塞。
GDB: UNIX及UNIX-like下的調(diào)試工具管呵。
LLDB: 開源的內(nèi)置于XCode的具有REPL(read-eval-print-loop)特征的Debugger,其可以安裝C++或者Python插件唤殴。
兩個都是調(diào)試用的Debugger般婆,只是LLDB是比較高級的版本,或者在調(diào)試開發(fā)iOS應用時比較好用朵逝,lldb與gdb命令名的對照表:http://lldb.llvm.org/lldb-gdb.html
開始使用LLDB
在什么地方可以輸入這個命令蔚袍?首先, 在程序里你需要的地方設置斷點。當斷點斷住的時候你就能看到我們進入LLDB調(diào)試器了配名。
這時就可以使用一些LLDB命令來進行一些調(diào)試了页响。
一些Xcode調(diào)試快捷鍵:command+shift+Y 打開調(diào)試窗口command+Y 調(diào)試運行程序command+option+P 繼續(xù)command+shift+O 跳過command+shift+I 進入command+shift+T 跳出
常用命令
help
最簡單命令 help 會列舉出所有的命令。如果你忘記了一個命令是做什么的段誊,或者想知道更多的話闰蚕,你可以通過 help <command> 來了解更多細節(jié),例如 help print 或者 help thread连舍。
print
試試 print 命令:
LLDB 實際上會作前綴匹配没陡。所以你也可以使用 prin,pri索赏,或者 p盼玄。但你不能使用 pr,因為 LLDB 不能消除和 process 的歧義潜腻。
結果中有個 $0埃儿。實際上你可以使用它來指向這個結果。試試 print $0 + 7融涣,你會看到 106童番。任何以美元符開頭的東西都是存在于 LLDB 的命名空間的,它們是為了幫助你進行調(diào)試而存在的威鹿。
輸出view 下 subview 的數(shù)量
//由于 subview 的數(shù)量是一個 int 類型的值剃斧,所以我們使用命令p:(lldb)p (int)[[[self view] subviews] count]
直接調(diào)用方法改變背景顏色之類
其實使用p,po忽你,call都可以調(diào)用方法幼东,只是p和po都是用于輸出的有返回值的。call一般只在不需要顯示輸出,或是方法無返回值時使用根蟹。(lldb)p [self.view setBackgroundColor:[UIColor redColor]](lldb)p (void)[CATransaction flush]上述的p一般使用call比較好脓杉,因為方法是沒有返回值的。
p objects
命令p objects跟p很像简逮。p輸出的是基本類型丽已,po輸出的Objective-C對象。調(diào)試器會輸出這個 object 的 description买决。po (print object 的縮寫):
expression
如果想改變一個值怎么辦沛婴?其實這時候我們要用到的是 expression 這個方便的命令。expression的簡寫就是e督赤∴业疲可以用expression來聲明新的變量,也可以改變已有變量的值躲舌。我們看到e聲明的都是$開頭的變量丑婿。我們在使用時也需要加上$符號。
創(chuàng)建新的變量示例:
注意:如果上面這里輸入以下命令没卸,會發(fā)生錯誤羹奉。說明lldb無法判定某一步的計算結果是什么數(shù)據(jù)類型,這時需要強制類型轉換來告訴lldb约计。
(lldb) p [[$array objectAtIndex:0] characterAtIndex:0]error: no known method '-characterAtIndex:'; cast the message send to the method's return typeerror: 1 errors parsing expression(lldb) p (char)[[$array objectAtIndex:0] characterAtIndex:0]'o'
修改已有變量示例:
image
image 命令可用于尋址诀拭,有多個組合命令。比較實用的用法是用于尋找棧地址對應的代碼位置煤蚌。如:
NSArray *arr=[[NSArray alloc] initWithObjects:@"1",@"2", nil];NSLog(@"%@",arr[2]);
這段代碼有明顯的錯誤耕挨,程序運行這段代碼后會拋出下面的異常:
*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndex:]: index 2 beyond bounds [0 .. 1]'*** First throw call stack:( 0 CoreFoundation 0x0000000101951495 __exceptionPreprocess + 165 1 libobjc.A.dylib 0x00000001016b099e objc_exception_throw + 432 CoreFoundation 0x0000000101909e3f -[__NSArrayI objectAtIndex:] + 1753 ControlStyleDemo 0x0000000100004af8 -[RootViewController viewDidLoad] + 3124 UIKit 0x000000010035359e -[UIViewController loadViewIfRequired] + 5625 UIKit 0x0000000100353777 -[UIViewController view] + 296 UIKit 0x000000010029396b -[UIWindow addRootViewControllerViewIfPossible] + 587 UIKit 0x0000000100293c70 -[UIWindow _setHidden:forced:] + 2828 UIKit 0x000000010029cffa -[UIWindow makeKeyAndVisible] + 519 ControlStyleDemo 0x00000001000045e0 -[AppDelegate application:didFinishLaunchingWithOptions:] + 67210 UIKit 0x00000001002583d9 -[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:] + 26411 UIKit 0x0000000100258be1 -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 160512 UIKit 0x000000010025ca0c -[UIApplication _runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:] + 66013 UIKit 0x000000010026dd4c -[UIApplication handleEvent:withNewEvent:] + 318914 UIKit 0x000000010026e216 -[UIApplication sendEvent:] + 7915 UIKit 0x000000010025e086 _UIApplicationHandleEvent + 57816 GraphicsServices 0x0000000103aca71a _PurpleEventCallback + 76217 GraphicsServices 0x0000000103aca1e1 PurpleEventCallback + 3518 CoreFoundation 0x00000001018d3679 CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION + 4119 CoreFoundation 0x00000001018d344e __CFRunLoopDoSource1 + 47820 CoreFoundation 0x00000001018fc903 __CFRunLoopRun + 193921 CoreFoundation 0x00000001018fbd83 CFRunLoopRunSpecific + 46722 UIKit 0x000000010025c2e1 -[UIApplication _run] + 60923 UIKit 0x000000010025de33 UIApplicationMain + 101024 ControlStyleDemo 0x0000000100006b73 main + 11525 libdyld.dylib 0x0000000101fe95fd start + 126 ??? 0x0000000000000001 0x0 + 1)libc++abi.dylib: terminating with uncaught exception of type NSException
現(xiàn)在,我們懷疑出錯的地址是0x0000000100004af8(可以根據(jù)執(zhí)行文件名判斷尉桩,或者最小的棧地址)筒占。為了進一步精確定位,我們可以輸入以下的命令:
(lldb)image lookup --address 0x0000000100004af8(lldb)im loo -a 0x0000000100004af8
命令執(zhí)行后返回:
Address: ControlStyleDemo[0x0000000100004af8] (ControlStyleDemo.__TEXT.__text + 13288)Summary: ControlStyleDemo`-[RootViewController viewDidLoad] + 312 at RootViewController.m:53
可以看到蜘犁,出錯的位置是RootViewController.m
的第53行翰苫。
call
call即是調(diào)用的意思。其實上述的po和p也有調(diào)用的功能这橙。因此一般只在不需要顯示輸出奏窑,或是方法無返回值時使用call。和上面的命令一樣析恋,我們在viewDidLoad:里面設置斷點良哲,然后在程序中斷的時候輸入下面的命令:
call [self.view setBackgroundColor:[UIColor redColor]]
繼續(xù)運行程序盛卡,看看view的背景顏色是不是變成紅色的了助隧!在調(diào)試的時候靈活運用call命令可以起到事半功倍的作用。
bt
打印調(diào)用堆棧,加all可打印所有thread的堆棧并村。
流程控制命令
實際上使用xcode自帶的可視化工具來控制“繼續(xù)”“暫臀∈担”“下一步”“進入”“跳出”更簡單,但這里還是列出其所對應的命令名:
繼續(xù):process continue, continue, c
下一步:thread step-over, next, n
進入:thread step-in, step, s
跳出:thread step-out, finish, f
thread return
執(zhí)行thread return命令可以使得當前函數(shù)立即返回哩牍,也就是說棚潦,后續(xù)代碼都不會執(zhí)行了。當然執(zhí)行此命令可能會使得arc的計數(shù)追蹤出現(xiàn)錯亂膝昆。
thread return命令需要一個參數(shù)來指明函數(shù)強制返回時的返回值丸边。
斷點命令
斷點有很多進階使用方法:條件斷點、條件執(zhí)行荚孵、記錄日志妹窖、自動繼續(xù)、重復斷點跳過收叶。使用xcode提供的可視化工具來操作是很容易的:
調(diào)試中執(zhí)行任意代碼
(lldb) e char *$str = (char *)malloc(128)(lldb) e (void)strcpy($str, "wxrld of warcraft")(lldb) e $str[1] = 'o'(char) $0 = 'o'(lldb) p $str(char *) $str = 0x00007fd04a900040 "world of warcraft"(lldb) e (void)free($str)
所以骄呼,在debugger中可以修改view的顏色、尺寸判没、甚至創(chuàng)建controller來push蜓萄。
watchpoint
watchpoint可以在某個變量被寫入/讀取時暫停程序運行:
(lldb) watchpoint set expression -- (int*)&_abc4Watchpoint created: Watchpoint 7: addr = 0x15e36d3c size = 4 state = enabled type = w new value: 0x00000000(lldb) watchpoint set v -w read _abc4Watchpoint created: Watchpoint 8: addr = 0x15e36d3c size = 4 state = enabled type = r watchpoint spec = '_abc4' new value: 0(lldb) watchpoint set v -w read_write _abc3Watchpoint created: Watchpoint 9: addr = 0x15e36d38 size = 4 state = enabled type = rw watchpoint spec = '_abc3' new value: 0
實際上可以使用watchpoint來監(jiān)視任意一段內(nèi)存的讀寫。使用XCode也可以方便地創(chuàng)建watchpoint澄峰。
XCode的可視化debug工具中的watch是一個write類型watchpoint(也就是默認的)
另外嫉沽,上述語句中 v是variable的簡寫,同樣的俏竞,set可以簡寫為s耻蛇,watch可以簡寫為wa,而-w后面的參數(shù)是不可以簡寫的必須為read胞此、write或者read_write臣咖。
當前在arm和x86上,我們一次最多創(chuàng)建4個watchpoint漱牵,繼續(xù)創(chuàng)建會提示錯誤夺蛇。
查看內(nèi)存
使用XCode的可視化工具來查看memory,要注意watch memory of "p" 和watch memory of "*p"的區(qū)別酣胀。
手動執(zhí)行命令可以help x或者 help memory刁赦。