日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

LLDB基础知识

發布時間:2025/3/15 编程问答 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 LLDB基础知识 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

LLDB基礎知識

LLDB控制臺

Xcode中內嵌了LLDB控制臺,在Xcode中代碼的下方,我們可以看到LLDB控制臺。


LLDB控制臺平時會輸出一些log信息。如果我們想輸入命令調試,必須讓程序進入暫停狀態。讓程序進入暫停狀態的方式主要有2種:

  • 斷點或者watchpoint: 在代碼中設置一個斷點(watchpoint),當程序運行到斷點位置的時候,會進入stop狀態
  • 直接暫停,控制臺上方有一個暫停按鈕,上圖紅框已標出,點擊即可暫停程序
  • LLDB語法

    在使用LLDB之前,我們來先看看LLDB的語法,了解語法可以幫助我們清晰的使用LLDB:

    ?

    <command> [<subcommand> [<subcommand>...]] <action> [-options [option-value]] [argument [argument...]]

    一眼看上去可能比較迷茫,給大家解釋一下:

  • <command>(命令)和<subcommand>(子命令):LLDB調試命令的名稱。命令和子命令按層級結構來排列:一個命令對象為跟隨其的子命令對象創建一個上下文,子命令又為其子命令創建一個上下文,依此類推。
  • <action>:執行命令的操作
  • <options>:命令選項
  • <arguement>:命令的參數
  • []:表示命令是可選的,可以有也可以沒有
  • 舉個例子,假設我們給main方法設置一個斷點,我們使用下面的命令:

    breakpoint set -n main

    這個命令對應到上面的語法就是:

  • command:?breakpoint?表示斷點命令
  • action:?set?表示設置斷點
  • option:?-n?表示根據方法name設置斷點
  • arguement:?mian?表示方法名為mian
  • 原始(raw)命令

    LLDB支持不帶命令選項(options)的原始(raw)命令,原始命令會將命令后面的所有東西當做參數(arguement)傳遞。不過很多原始命令也可以帶命令選項,當你使用命令選項的時候,需要在命令選項后面加--區分命令選項和參數。

    e.g: 常用的expression就是raw命令,一般情況下我們使用expression打印一個東西是這樣的:

    (lldb) expression count (int) $2 = 4

    當我們想打印一個對象的時候。需要使用-O命令選項,我們應該用--將命令選項和參數區分:

    (lldb) expression -O -- self <ViewController: 0x7f9000f17660>

    唯一匹配原則

    LLDB的命令遵循唯一匹配原則:假如根據前n個字母已經能唯一匹配到某個命令,則只寫前n個字母等效于寫下完整的命令。
    e.g: 前面提到我設置斷點的命令,我們可以使用唯一匹配原則簡寫,下面2條命令等效:

    breakpoint set -n main br s -n main

    ~/.lldbinit

    LLDB有了一個啟動時加載的文件~/.lldbinit,每次啟動都會加載。所以一些初始化的事兒,我們可以寫入~/.lldbinit中,比如給命令定義別名等。但是由于這時候程序還沒有真正運行,也有部分操作無法在里面玩,比如設置斷點。

    LLDB命令

    expression

    expression命令的作用是執行一個表達式,并將表達式返回的結果輸出。expression的完整語法是這樣的:

    expression <cmd-options> -- <expr>
  • <cmd-options>:命令選項,一般情況下使用默認的即可,不需要特別標明。
  • --: 命令選項結束符,表示所有的命令選項已經設置完畢,如果沒有命令選項,--可以省略
  • <expr>: 要執行的表達式
  • 說expression是LLDB里面最重要的命令都不為過。因為他能實現2個功能。

    我們在代碼運行過程中,可以通過執行某個表達式來動態改變程序運行的軌跡。
    假如我們在運行過程中,突然想把self.view顏色改成紅色,看看效果。我們不必寫下代碼,重新run,只需暫停程序,用expression改變顏色,再刷新一下界面,就能看到效果

    // 改變顏色(lldb) expression -- self.view.backgroundColor = [UIColor redColor]// 刷新界面(lldb) expression -- (void)[CATransaction flush]

    也就是說我們可以通過expression來打印東西。
    假如我們想打印self.view:

    (lldb) expression -- self.view(UIView *) $1 = 0x00007fe322c18a10

    p & print & call

    一般情況下,我們直接用expression還是用得比較少的,更多時候我們用的是p、print、call。這三個命令其實都是expression --的別名(--表示不再接受命令選項,詳情見前面原始(raw)命令這一節)

  • print: 打印某個東西,可以是變量和表達式
  • p: 可以看做是print的簡寫
  • call: 調用某個方法。
  • 表面上看起來他們可能有不一樣的地方,實際都是執行某個表達式(變量也當做表達式),將執行的結果輸出到控制臺上。所以你可以用p調用某個方法,也可以用call打印東西
    e.g: 下面代碼效果相同:

    (lldb) expression -- self.view (UIView *) $5 = 0x00007fb2a40344a0 (lldb) p self.view (UIView *) $6 = 0x00007fb2a40344a0 (lldb) print self.view (UIView *) $7 = 0x00007fb2a40344a0 (lldb) call self.view (UIView *) $8 = 0x00007fb2a40344a0 (lldb) e self.view (UIView *) $9 = 0x00007fb2a40344a0 根據唯一匹配原則,如果你沒有自己添加特殊的命令別名。e也可以表示expression的意思。原始命令默認沒有命令選項,所以e也能帶給你同樣的效果

    po

    我們知道,OC里所有的對象都是用指針表示的,所以一般打印的時候,打印出來的是對象的指針,而不是對象本身。如果我們想打印對象。我們需要使用命令選項:-O。為了更方便的使用,LLDB為expression -O --定義了一個別名:po

    (lldb) expression -- self.view (UIView *) $13 = 0x00007fb2a40344a0 (lldb) expression -O -- self.view <UIView: 0x7fb2a40344a0; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7fb2a4018c80>> (lldb) po self.view <UIView: 0x7fb2a40344a0; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7fb2a4018c80>>

    thread

    thread backtrace?& bt

    有時候我們想要了解線程堆棧信息,可以使用thread backtrace
    thread backtrace作用是將線程的堆棧打印出來。我們來看看他的語法

    thread backtrace [-c <count>] [-s <frame-index>] [-e <boolean>]

    thread backtrace后面跟的都是命令選項:

    -c:設置打印堆棧的幀數(frame)
    -s:設置從哪個幀(frame)開始打印
    -e:是否顯示額外的回溯
    實際上這些命令選項我們一般不需要使用。
    e.g: 當發生crash的時候,我們可以使用thread backtrace查看堆棧調用

    (lldb) thread backtrace * thread #1: tid = 0xdd42, 0x000000010afb380b libobjc.A.dylib`objc_msgSend + 11, queue = ‘com.apple.main-thread‘, stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)frame #0: 0x000000010afb380b libobjc.A.dylib`objc_msgSend + 11* frame #1: 0x000000010aa9f75e TLLDB`-[ViewController viewDidLoad](self=0x00007fa270e1f440, _cmd="viewDidLoad") + 174 at ViewController.m:23frame #2: 0x000000010ba67f98 UIKit`-[UIViewController loadViewIfRequired] + 1198frame #3: 0x000000010ba682e7 UIKit`-[UIViewController view] + 27frame #4: 0x000000010b93eab0 UIKit`-[UIWindow addRootViewControllerViewIfPossible] + 61frame #5: 0x000000010b93f199 UIKit`-[UIWindow _setHidden:forced:] + 282frame #6: 0x000000010b950c2e UIKit`-[UIWindow makeKeyAndVisible] + 42

    我們可以看到crash發生在-[ViewController viewDidLoad]中的第23行,只需檢查這行代碼是不是干了什么非法的事兒就可以了。

    LLDB還為backtrace專門定義了一個別名:bt,他的效果與thread backtrace相同,如果你不想寫那么長一串字母,直接寫下bt即可

    (lldb) bt thread return Debug的時候,也許會因為各種原因,我們不想讓代碼執行某個方法,或者要直接返回一個想要的值。這時候就該thread return上場了。

    ?

    thread return可以接受一個表達式,調用命令之后直接從當前的frame返回表達式的值。

    e.g: 我們有一個someMethod方法,默認情況下是返回YES。我們想要讓他返回NO

    我們只需在方法的開始位置加一個斷點,當程序中斷的時候,輸入命令即可:

    (lldb) thread return NO

    效果相當于在斷點位置直接調用return NO;,不會執行斷點后面的代碼

    c & n & s & finish

    一般在調試程序的時候,我們經常用到下面這4個按鈕:

    用觸摸板的孩子們可能會覺得點擊這4個按鈕比較費勁。其實LLDB命令也可以完成上面的操作,而且如果不輸入命令,直接按Enter鍵,LLDB會自動執行上次的命令。按一下Enter就能達到我們想要的效果,有木有頓時感覺逼格滿滿的!!!

    我們來看看對應這4個按鈕的LLDB命令:

  • c/?continue/?thread continue: 這三個命令效果都等同于上圖中第一個按鈕的。表示程序繼續運行
  • n/?next/?thread step-over: 這三個命令效果等同于上圖第二個按鈕。表示單步運行
  • s/?step/?thread step-in: 這三個命令效果等同于上圖第三個按鈕。表示進入某個方法
  • finish/?step-out: 這兩個命令效果等同于第四個按鈕。表示直接走完當前方法,返回到上層frame
  • thread其他不常用的命令

    thread 相關的還有其他一些不常用的命令,這里就簡單介紹一下即可,如果需要了解更多,可以使用命令help thread查閱

  • thread jump: 直接讓程序跳到某一行。由于ARC下編譯器實際插入了不少retain,release命令。跳過一些代碼不執行很可能會造成對象內存混亂發生crash。
  • thread list: 列出所有的線程
  • thread select: 選擇某個線程
  • thread until: 傳入一個line的參數,讓程序執行到這行的時候暫停
  • thread info: 輸出當前線程的信息
  • frame

    前面我們提到過很多次frame(幀)。可能有的朋友對frame這個概念還不太了解。隨便打個斷點

    ?

    我們在控制臺上輸入命令bt,可以打印出來所有的frame。如果仔細觀察,這些frame和左邊紅框里的堆棧是一致的。平時我們看到的左邊的堆棧就是frame。

    frame variable

    平時Debug的時候我們經常做的事就是查看變量的值,通過frame variable命令,可以打印出當前frame的所有變量

    (lldb) frame variable (ViewController *) self = 0x00007fa158526e60 (SEL) _cmd = "text:" (BOOL) ret = YES (int) a = 3

    可以看到,他將self,_cmd,ret,a等本地變量都打印了出來

    如果我們要需要打印指定變量,也可以給frame variable傳入參數:

    (lldb) frame variable self->_string (NSString *) self->_string = nil

    不過frame variable只接受變量作為參數,不接受表達式,也就是說我們無法使用frame variable self.string,因為self.string是調用string的getter方法。所以一般打印指定變量,我更喜歡用p或者po。

    其他不常用命令

    一般frame variable打印所有變量用得比較多,frame還有2個不怎么常用的命令:

    frame info: 查看當前frame的信息

    (lldb) frame info frame #0: 0x0000000101bf87d5 TLLDB`-[ViewController text:](self=0x00007fa158526e60, _cmd="text:", ret=YES) + 37 at

    frame select: 選擇某個frame

    (lldb) frame select 1 frame #1: 0x0000000101bf872e TLLDB`-[ViewController viewDidLoad](self=0x00007fa158526e60, _cmd="viewDidLoad") + 78 at ViewController.m:2320 21 - (void)viewDidLoad {22 [super viewDidLoad]; -> 23 [self text:YES];24 NSLog(@"1");25 NSLog(@"2");26 NSLog(@"3");

    當我們選擇frame 1的時候,他會把frame1的信息和代碼打印出來。不過一般我都是直接在Xcode左邊點擊某個frame,這樣更方便

    breakpoint

    調試過程中,我們用得最多的可能就是斷點了。LLDB中的斷點命令也非常強大

    breakpoint set

    breakpoint set命令用于設置斷點,LLDB提供了很多種設置斷點的方式:

    使用-n根據方法名設置斷點:

    e.g: 我們想給所有類中的viewWillAppear:設置一個斷點:

    (lldb) breakpoint set -n viewWillAppear:Breakpoint 13: 33 locations.

    使用-f指定文件

    e.g: 我們只需要給ViewController.m文件中的viewDidLoad設置斷點:

    (lldb) breakpoint set -f ViewController.m -n viewDidLoadBreakpoint 22: where = TLLDB`-[ViewController viewDidLoad] + 20 at ViewController.m:22, address = 0x000000010272a6f4

    這里需要注意,如果方法未寫在文件中(比如寫在category文件中,或者父類文件中),指定文件之后,將無法給這個方法設置斷點。

    使用-l指定文件某一行設置斷點

    e.g: 我們想給ViewController.m第38行設置斷點

    (lldb) breakpoint set -f ViewController.m -l 38 Breakpoint 23: where = TLLDB`-[ViewController text:] + 37 at ViewController.m:38, address = 0x000000010272a7d5

    使用-c設置條件斷點

    e.g:?text:方法接受一個ret的參數,我們想讓ret == YES的時候程序中斷:

    (lldb) breakpoint set -n text: -c ret == YES Breakpoint 7: where = TLLDB`-[ViewController text:] + 30 at ViewController.m:37, address = 0x0000000105ef37ce 使用-o設置單次斷點e.g: 如果剛剛那個斷點我們只想讓他中斷一次: (lldb) breakpoint set -n text: -o ‘breakpoint 3‘: where = TLLDB`-[ViewController text:] + 30 at ViewController.m:37, address = 0x000000010b6f97ce

    breakpoint command

    有的時候我們可能需要給斷點添加一些命令,比如每次走到這個斷點的時候,我們都需要打印self對象。我們只需要給斷點添加一個po self命令,就不用每次執行斷點再自己輸入po self了

    breakpoint command add

    breakpoint command add命令就是給斷點添加命令的命令。

    e.g: 假設我們需要在ViewController的viewDidLoad中查看self.view的值
    我們首先給-[ViewController viewDidLoad]添加一個斷點

    (lldb) breakpoint set -n "-[ViewController viewDidLoad]" ‘breakpoint 3‘: where = TLLDB`-[ViewController viewDidLoad] + 20 at ViewController.m:23, address = 0x00000001055e6004

    可以看到添加成功之后,這個breakpoint的id為3,然后我們給他增加一個命令:po self.view

    (lldb) breakpoint command add -o "po self.view" 3

    -o完整寫法是--one-liner,表示增加一條命令。3表示對id為3的breakpoint增加命令。
    添加完命令之后,每次程序執行到這個斷點就可以自動打印出self.view的值了

    如果我們一下子想增加多條命令,比如我想在viewDidLoad中打印當前frame的所有變量,但是我們不想讓他中斷,也就是在打印完成之后,需要繼續執行。我們可以這樣玩:

    (lldb) breakpoint command add 3 Enter your debugger command(s). Type ‘DONE‘ to end. > frame variable > continue > DONE

    輸入breakpoint command add 3對斷點3增加命令。他會讓你輸入增加哪些命令,輸入‘DONE‘表示結束。這時候你就可以輸入多條命令了

    breakpoint command list

    如果想查看某個斷點已有的命令,可以使用breakpoint command list。
    e.g: 我們查看一下剛剛的斷點3已有的命令

    轉載于:https://www.cnblogs.com/jingjin/p/6239484.html

    總結

    以上是生活随笔為你收集整理的LLDB基础知识的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。