BreakPoint & LLDB

前言

記錄一下比較常用的一些 LLDB 調試技巧.
Note: 在這里是記錄一下常用的方法, 并不是完全教程喲!
Note: 前方多圖(前方高能), 流量慎入. 土豪無視.

BreakPoint


工作中使用斷點對程序進行調試可以說就跟家常便飯一樣, 我們幾乎天天都會用到. 來看看 Xcode 中如何簡單的使用斷點吧.
在 Xcode 中設置斷點的方法有如下三種:

  1. 在代碼左側的行數那一列中點擊一下, 就會出現一個斷點.
  2. 用鼠標選中你希望下斷點的一行, 然后按 Command + \ 來設置斷點.
  3. 使用 LLDB 指令生成斷點.

下圖中我們看到的藍色的矩形, 就是表示該行設置了斷點.

設置斷點

如果希望一個斷點暫時失效, 點擊藍色矩形區域, 此時藍色將會變為灰色, 表示斷點失效, 如下圖:

使斷點失效

刪除一個斷點也很簡單, 用鼠標拖住矩形區域, 在代碼區域放手就可以了, 此時你會看到一個動效并且會聽到的一聲. 如下圖紅色矩形內的動效:

刪除一個斷點

對一個斷點進行編輯, 只需要鼠標右鍵點擊斷點, 然后選擇Edit Breakpoint , 如下圖:

編輯斷點

進入斷點編輯模式后, 我們將會看到如下的對話框:

在這里編輯斷點的具體內容

在這里我們可以對斷點進行編輯.
Condition: 條件, 這里可以設置斷點的出發條件, 例如我們在程序中有一個變量名為 index, 在這里我們設置條件 index == 1000, 代表只有當 index 變量為 1000的時候, 斷點才會被觸發.
Ignore: 忽略, 在這里可以設置斷點被忽略多少次以后觸發.
Action: 我們可以為斷點觸發的時候添加事件, 譬如: 語音啊, 音效啊, LLDB 指令之類的. 我們會在實戰環節使用 Action 來看看效果哈, 不要急.
Options: 把這個對勾勾上的話, 程序不會終止在斷點的位置, 而是會繼續運行.
來看看下面這個例子:

- (void) addLabel {
    
    UILabel *label = [[UILabel alloc] init];
    label.text = @"Test LLDB";
    [self.view addSubview: label];
}

- (void) touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [self addRedView];
}

我們寫了一個方法 addLabel, 用來添加一個 label對象, 每次點擊 self.view 都會觸發 addLabel 方法, 可以看到代碼中我們并沒有設置 label 對象的 frame, 所以無論我們如何點擊屏幕, label 都是無法顯示出來的, 我們現在為 [self.view addSubview: label] 這句代碼所在的行下一個斷點, 然后編輯這個斷點, 如下圖所示:

對斷點進行編輯后, 是這個樣子的

我來解釋一下這個斷點的意義:

  1. 首先是條件, 只有在 label.text@"Test LLDB"的時候, 斷點才會被觸發.
  2. 其次是忽略次數, 該斷點會被忽略兩次, 也就是你前兩次點擊屏幕的時候, 該斷點是被忽略掉的.
  3. 接下來是事件, 在這個斷點中, 我添加了三個事件, Sound 事件: 觸發斷點會有一個提示音效. Shell Command 事件: 我這里設置的是 say, 就是將下面那句話讀出來, %H 代表斷點被Hit 的次數. Debugger Command 事件: 可以添加 LLDB 指令, 在這里我們執行了一句代碼, 給 label 對象的 frame 屬性進行了賦值. Log Message 事件: 在這個事件中, 你可以選擇將你輸入的文字打印到控制臺中 或 讀出來. %H 代表斷點被 Hit 的次數, %B 代表函數名.
  4. 最后自動繼續運行程序.

除了普通的斷點, Xcode 還提供其他類型的斷點, 例如 Exception Breakpoint, 我們調試程序的過程中, 我相信很多小伙伴都遇到過那種非常頭疼的 Crash, 就是程序直接Crash 到了 main 函數中, 這種問題相當的不好定位, 此時可以添加一個 Exception Breakpoint斷點來捕獲異常.

![設置 Exception Breakpoint]
](http://upload-images.jianshu.io/upload_images/2452150-1e05b780abe42e61.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

在 Xcode 中使用 Command + 7 來查看當前程序中的所有斷點. 在這里我們也可以管理斷點, 例如刪除斷點, 關閉斷點, 編輯斷點等操作. 如下圖:

管理斷點

來看看下面這張圖, 我來依次介紹下圖中按鈕的功能:

控制臺上面的功能按鈕

按鈕從左至右:

  1. 收起控制臺
  2. 開啟/關閉 所有斷點: 藍色代表開啟, 灰色代表關閉
  3. 暫停/繼續 程序: 該按鈕默認行為是暫停應用程序, 如果程序當前處于暫停狀態, 那么點擊該按鈕為允許程序正常執行下去(一直執行下去, 或遇到下一個斷點).
  4. 下一步: 會以黑盒的方式執行一行代碼。如果所在這行代碼是一個函數調用,那么就不會跳進這個函數,而是會執行這個函數,然后繼續。
  5. 進入函數
  6. 退出函數

這幾個按鈕的作用, 大家在程序中下個斷點自己點點試試就知道了, 很簡單的. 這就是 Xcode 中斷點的最最最基本的應用.

LLDB


介紹

語法

po & p

  • po: 打印一個 Objective-C 對象, po 指令實際上是expression -O -- 指令的別名.
  • p: 打印類似 intfloat 等基本數據類型和類似 CGRectCGPoint 等結構體. (pprint 的縮寫, 你還可以使用printprinpri)

看下面這個代碼塊, 在 -(void) viewDidLoad 方法中聲明了幾個變量, 我們用 pop 來打印一下看看效果.

// ViewController.h
@interface ViewController ()
@property (nonatomic, strong) UILabel *titleLabel;
@end


// ViewController.m 
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 1. 聲明兩個結構體
    CGRect  rect  = CGRectMake(0, 0, 100, 100);
    CGPoint point = CGPointMake(100, 100);
    
    // 2. 聲明兩個基本數據類型
    NSInteger index = 100;
    CGFloat width = 200.0f;

    // 3. 聲明兩個 Objective-C 對象
    NSArray *array = [NSArray arrayWithObjects: self.titleLabel, @"LLDBDemo", nil];
    NSDictionary *dictionary = @{
                                 @"kMLObject" : self.titleLabel,
                                 @"kMLTitle" : @"LLDBDemo",
                                 };

    // 4. 創建 titleLabel
    self.titleLabel = [UILabel new];
    self.titleLabel.text = @"LLDBDemo";
    self.titleLabel.font = [UIFont systemFontOfSize: 16];
    self.titleLabel.textColor = [UIColor blackColor];
    [self.view addSubview: self.titleLabel];

    // 在這里打一個斷點
}
    
@end

ok, 代碼片段看完了, Command+R運行程序, 當程序運行到斷點終止時, 我們可以再控制臺使用 pop 命令來打印我們剛才聲明的變量. 效果如下:

(lldb) p rect
(CGRect) $0 = (origin = (x = 0, y = 0), size = (width = 100, height = 100))

(lldb) p point
(CGPoint) $1 = (x = 100, y = 100)

(lldb) p index
(NSInteger) $2 = 100

(lldb) p width
(CGFloat) $3 = 200

(lldb) po array
<__NSArrayI 0x610000229580>(
<UILabel: 0x7fb5a2d0b6a0; frame = (0 0; 0 0); text = 'LLDBDemo'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x60800008c760>>,
LLDBDemo
)


(lldb) po dictionary
{
    kMLObject = "<UILabel: 0x7fb5a2d0b6a0; frame = (0 0; 0 0); text = 'LLDBDemo'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x60800008c760>>";
    kMLTitle = LLDBDemo;
}

(lldb) po self.titleLabel
<UILabel: 0x7fb5a2d0b6a0; frame = (0 0; 0 0); text = 'LLDBDemo'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x60800008c760>>

除了以上你看到的這些打印, 你還可以打印更細節的東西, 以上面代碼塊中的 titleLabelarray 為例:

(lldb) po self.titleLabel.frame
(origin = (x = 0, y = 0), size = (width = 0, height = 0))

(lldb) po self.titleLabel.frame.size.width
0

(lldb) po self.titleLabel.text
LLDBDemo

(lldb) po [array objectAtIndex: 1]
LLDBDemo

可以看到 pop 的功能已經很強大了是不?

expression & expr & e

在我們調試程序的時候, 經常會有需要修改一個變量值得場景. 普通的調試方法, 我們可能會添加一行代碼, 然后重新 Command + R 運行程序, 但這必然會無畏的消耗很多時間. 此時使用 expression 指令就非常的方便. 舉例來說: 我們創建一個 UIView 實例, 添加到 self.view 中, 代碼如下:

    UIView *redView = [[UIView alloc] init];
    redView.frame = CGRectMake(20, 40, 100, 100);
    redView.backgroundColor = [UIColor redColor];
    [self.view addSubview: redView];

我們可以在 [self.view addSubview: redView]; 這行代碼的位置下一個斷點, 然后執行如下的兩行命令:

e redView.frame = CGRectMake(100, 100, 200, 200)
e redView.backgroundColor = [UIColor blueColor]

然后讓程序繼續運行起來看看效果, 可以看到原本應該為紅色的 view 現在變成了藍色, 并且 view 原本的位置和大小也發生了改變. 所以expression 指令不僅會改變調試器中的值, 它實際上是真正的改變了程序中的值, 有了這個東西, 代碼調試起來可就太爽了. 有些時候你可能不想繼續運行程序, 但是仍然想看到你修改的效果, 那怎么辦? 此時就應該執行完你的修改之后, 刷新一下界面, 代碼如下:

e redView.frame = CGRectMake(100, 100, 200, 200)
e redView.backgroundColor = [UIColor blueColor]
e [CATransaction flush]

刷新頁面之后, 你無需繼續運行程序, 就可以馬上看到效果.

call

call 指令代表著調用某個方法. 實際上callpprint這三個指令都是 expression 指令的別名, 實際上的運行效果是一樣的, 舉例來說明, 看如下代碼塊:

(lldb) print self.view
(UIView *) $2 = 0x00007f9769509660
(lldb) expression self.view
(UIView *) $3 = 0x00007f9769509660
(lldb) call self.view
(UIView *) $4 = 0x00007f9769509660
(lldb) e self.view
(UIView *) $5 = 0x00007f9769509660
(lldb) p self.view
(UIView *) $6 = 0x00007f9769509660

可以很清楚地看到, 這幾個指令實際上的效果是一樣的.

$符號

上文中簡介p 的時候, 我們看到代碼塊中會有這樣的東西, 例如: (NSInteger) $2 = 100(CGFloat) $3 = 200. 這些以$符號開頭的東東實際就是 LLDB 的命名空間的產物, 我們可以用這些東東來進行調試, 來看看下面這段:

// ViewController.m 
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    CGRect  rect  = CGRectMake(0, 0, 100, 100);

    // 在這里打一個斷點
}
    
@end

viewDidLoad 中隨便聲明了一個CGRect 變量, 然后在下方打上一個斷點, 我們用如下指令調試一下:

(lldb) p rect
(CGRect) $3 = (origin = (x = 0, y = 0), size = (width = 100, height = 100))
(lldb) e $3 = CGRectMake(10,10,10,10)
(CGRect) $4 = (origin = (x = 10, y = 10), size = (width = 10, height = 10))
(lldb) p rect
(CGRect) $5 = (origin = (x = 10, y = 10), size = (width = 10, height = 10))
(lldb) 

可以看到, 當我們 p rect 的時候, 打印的值是我們最初賦的值, 然后我們給 $3賦了一個CGRectMake(10,10,10,10)之后, 再來 p rect, 可以看到此時的 rect 變量已經被我們改變了.

Variable 變量

在某些場景中, 我們調試代碼的時候可能需要創建新的變量來輔助我們, 此時我們仍然不需要修改代碼后Command+R 重新跑程序, LLDB 同樣提供了相應的方法. 我們可以像正常寫代碼一樣, 創建一個 UIView 的實例, 然后將它添加到 self.view 中, 但是唯一不同的是, 聲明的變量名需要以美元符號$開頭. 看下面這個代碼塊:

(lldb) expression UIView *$view = [[UIView alloc] init]
(lldb) expression $view.backgroundColor = [UIColor blackColor]
(lldb) expression $view.frame = CGRectMake(0, 300, 100, 100);
(lldb) expression [self.view addSubview: $view]

此時我們將程序繼續運行, self.view 中就會新增了一個黑色的view.

thread backtrace & bt 查看調用堆棧

bt 指令就是查看調用堆棧的信息, 調用堆棧信息在程序運行到斷點時 或 崩潰時, 在 Xcode 左側導航區域中會自動顯示出來, 如下圖:

Xcode 調用堆棧

圖中可以清晰的看到, 程序當前正處于 Thread1addRedView方法中 (這玩意不會看的請自行 Google吧寶貝兒) . 除了在 Xcode 左側導航區域中我們可以查看調用堆棧, 我們同樣還可以使用 LLDB 為我們提供的 bt 指令進行查看. bt 指令只會查看當前線程的調用堆棧, 如果你希望查看全部的調用堆棧, 那么就需要使用 bt all 指令了. (Note: 左側數字代表了堆棧塊的編號, 這個一會我們會用到.)

frame 相關指令

講解 frame 相關指令之前, 先來看一小段示例代碼:

- (void) viewDidLoad {
    [super viewDidLoad];
    
    // 1. Create Blue View
    UIView *blueView = [[UIView alloc] initWithFrame: CGRectMake(20, 20, 100, 100)];
    [blueView setBackgroundColor: [UIColor blueColor]];
    [self.view addSubview: blueView];
    
    // 2. Add Red View
    [self addRedView];
}

- (void) addRedView {
    
    UIView *redView = [[UIView alloc] initWithFrame: CGRectMake(20, 140, 100, 100)];
    [redView setBackgroundColor: [UIColor redColor]];
    [self.view addSubview: redView]; // 斷點所在行
}

Command+R 運行, 程序將會停止在 [self.view addSubview: redView]; 這一行.

frame info & fr info

查看當前堆棧信息, 以上文提到示例代碼為例執行以下命令:

(lldb) frame info
frame #0: 0x000000010a952676 LLDBDemo`-[ViewController addRedView](self=0x00007f8f28409eb0, _cmd="addRedView") + 230 at ViewController.m:88

可以看到, frame info 指令可以查看當前所在堆棧的信息. 其中包括方法名文件名行號等信息.

frame select & fr sel

在工作中, 我們可能會有這樣的需求, 在調試一個相對較復雜的程序時, 我們可能會打很多斷點, 然后一個斷點一個斷點的追, 但是有時操作失誤錯過了某個斷點, 我們又要重新來過, 這同樣會消耗很多無畏的時間. 以上文的示例代碼為例, 此時程序由于斷點的原因停在了 [self.view addSubview: redView]; 這一行, 并且剛才我們也使用 frame info 指令查看了當前的堆棧信息, 我們當前處在addRedView方法中, 如果此時我希望修改 viewDidLoad 方法中的 blueView變量怎么辦呢? 我用先 po 一下試試:

(lldb) po blueView
error: use of undeclared identifier 'blueView'

結果顯然是不行的, 因為當前堆棧中, 并沒有 blueView, 也就是說如果我們希望對 blueView 進行任何操作, 我們需要做的第一步, 就是切換到 blueView 對應的堆棧當中, 那我們如何切換呢? 還記得bt 指令么? 我們先用 bt 指令查看一下調用堆棧信息, 如下圖:

上面這張圖我只截取了一部分, 可以看到 *代表的就是當前堆棧. 還可以看出, viewDidLoad 方法的堆棧編號為 #1. 此時我們使用指令frame select 1 就能切換到 viewDidLoad 方法所在的堆棧塊中:

(lldb) frame select 1
frame #1: 0x0000000108124502 LLDBDemo`-[ViewController viewDidLoad](self=0x00007f8d95905bc0, _cmd="viewDidLoad") + 354 at ViewController.m:81
   78       [self.view addSubview: blueView];
   79       
   80       // 2. Add Red View
-> 81       [self addRedView];
   82   }
   83   
   84   - (void) addRedView {

我們切換到了 blueView 對應的堆棧中, 就可以對blueView 變量進行想要的操作了. 例如:

(lldb) po blueView
<UIView: 0x7f8d9350b660; frame = (20 20; 100 100); layer = <CALayer: 0x608000028c80>>

Perfect! 完美!

thread return

thread return 指令有一個可選參數, 該參數接收一個表達之, 調用thread return 指令后將會直接跳出當前棧幀, 并且返回表達式的值. 這意味這函數剩余的部分不會被執行。這會給 ARC 的引用計數造成一些問題,或者會使函數內的清理部分失效。但是在函數的開頭執行這個命令,是個非常好的隔離這個函數,偽造返回值的方式 。(查看中文原文, 英文原文)

假設我們有一個方法, 是用來判斷傳入的 MLPerson 對象是否是男生的, 但是現在我們希望該方法, 無論何時都返回 YES, 此時我們只需要在該方法的最前面下一個斷點, 然后執行 thread return YES 就 OK 了, 如下:

- (BOOL) isBoy:(MLPerson *) person {
    
  // 在這里下一個斷點, 并且執行 thread return YES 指令.
    
    return person.gender;
}

breakpoint

本文最開始的部分已經介紹過如何使用 Xcode 的 UI 界面來設置斷點, 在這部分介紹如何使用 LLDB 來下斷點. (breakpoint 這一部分內容出自這里).

breakpoint set -n

根據方法名設置斷點, 假如我們希望給所有類中的 addLabel 方法下一個斷點:

(lldb) breakpoint set -n addLabel
Breakpoint 2: 4 locations.
breakpoint set -f

針對某一文件中的某一方法設置斷點, 如果方法沒有寫在文件中(例如父類中, Category 中), 那么設置該斷點將會失敗.

(lldb) breakpoint set -f ViewController.m -n addLabel 
Breakpoint 3: where = LLDBDemo`-[ViewController addLabel] + 16 at ViewController.m:93, address = 0x000000010b81f480
breakpoint set -l

針對某一文件中的某一行設置斷點.

(lldb) breakpoint set -f ViewController.m -l 101
Breakpoint 5: where = LLDBDemo`-[ViewController touchesBegan:withEvent:] + 96 at ViewController.m:101, address = 0x000000010b81f5a0
breakpoint set -c

設置條件斷點(對于條件斷點不明確的小伙伴請在本文中第一部分查看).

(lldb) breakpoint set -f ViewController.m -n isNilString: -c string.length
Breakpoint 9: where = LLDBDemo`-[ViewController isNilString:] + 39 at ViewController.m:107, address = 0x000000010b81f637
breakpoint set -o

設置一個單次斷點, 該斷點只會觸發一次:

(lldb) breakpoint set -f ViewController.m -n addLabel -o
Breakpoint 10: where = LLDBDemo`-[ViewController addLabel] + 16 at ViewController.m:93, address = 0x000000010b81f480
breakpoint list

使用該指令查看設置了哪些斷點, 如下:

(lldb) br li 
Current breakpoints:
19: name = 'addLabel', locations = 1, resolved = 1, hit count = 4
  19.1: where = LLDBDemo`-[ViewController addLabel] + 16 at ViewController.m:93, address = 0x000000010b81f480, resolved, hit count = 4 
breakpoint disable & breakpoint enable

我們可以使用這兩個指令設置斷點是否開啟(是否可用), 如下:

// 讓斷點 19 暫時失效
(lldb) breakpoint disable 19
1 breakpoints disabled.

// 讓斷點 19 生效
(lldb) breakpoint enable 19
1 breakpoints enabled.
breakpoint delete

該指令代表刪除斷點, 我們可以刪除對應編號的斷點, 如下:

(lldb) breakpoint delete 19
1 breakpoints deleted; 0 breakpoint locations disabled.

我們也可以刪除所有的斷點, 刪除所有斷點的時候, 我們會得到一個提示, 讓我們確認是否刪除所有斷點, 此時我們輸入y代表確認刪除, 如下:

(lldb) breakpoint delete
About to delete all breakpoints, do you want to do that?: [Y/n] y
All breakpoints removed. (1 breakpoint)

如果你覺得這個提示太煩了, 你也可以使用 -f 指令來直接刪除所有斷點, 如下:

(lldb) breakpoint delete -f
All breakpoints removed. (1 breakpoint)

breakpoint command

在某些特定的時候, 當一個斷點被觸發了之后, 我們可能需要執行一些指令. 比如每次觸發斷點, 我們都會打印一下堆棧信息, 此時我們可以為斷點需要添加bt 指令, 這樣就可以避免每次觸發斷點后, 我們再手動輸入指令了.

breakpoint command add

想為一個斷點添加命令, 首先我們必須要創建一個斷點, 如下:

(lldb) breakpoint set -f ViewController.m -n addLabel 
Breakpoint 19: where = LLDBDemo`-[ViewController addLabel] + 16 at ViewController.m:93, address = 0x000000010b81f480

通過設置斷點, 我們可以看到, 當前這個斷點的編號為 19. 那么接下來, 我們就為編號為19的斷點添加指令, 如下:

(lldb) breakpoint command add -o "bt" 19

此時, 編號為 19 的斷點, 就已經增加了一條 bt 指令, 當每次觸發該斷點的時候, 都會在控制臺輸出堆棧信息. 在上面代碼塊中的 -o指令的完整寫法是--one-liner, 表示增加一條指令. 如果我們需要給該斷點增加更多的指令, 此時我們就不要使用 -o 命令了, 應該像如下這么寫:

(lldb) breakpoint command add 19
Enter your debugger command(s).  Type 'DONE' to end.
> bt
> continue
> DONE

在這里我們為編號為 19 的斷點增加了兩條指令分別是 btcontinue, 當我們指令輸入完畢, 再輸入 DONE 就代表結束. Note: 多次對同一個斷點添加指令, 后面的指令則會覆蓋前面的指令.

breakpoint command list

使用該指令, 可以查看某一斷點中附加的指令, 我們嘗試查看一下編號為 19 的斷點中附加的指令, 如下:

(lldb) breakpoint command list 19
Breakpoint 19:
    Breakpoint commands:
      bt
      continue
breakpoint command delete

使用該指令, 可以刪除某一斷點中附加的指令, 我們嘗試刪除一下編號為 19 的斷點中附加的指令, 如下:

(lldb) breakpoint command delete 19
(lldb) breakpoint command list 19
Breakpoint 19 does not have an associated command.

breakpoint這一部分中的命令, 其實完全可以使用 Xcode 為我們提供的 UI 界面來實現, 更直觀, 更方便. 所以這部分基本上就是從這篇文章中摘抄過來的. 有興趣的同學可以看看原文, 寫的還是挺 Nice 的.

流程控制

流程控制這個東西, 實際上上文中也有提到過, 還記得這張圖么:

如果已經忘了這幾個按鈕的作用了, 那就翻到本文最初的位置進行查看.
按鈕從左往右依次對應的指令為:

  1. process continue & continue & c
  2. thread step-over & next & n
  3. thread step-in & step & s
  4. step-out & finish

常用快捷鍵

Note: 這部分同樣出自這里

功能 命令
暫停/繼續 Command + Ctrl + Y
斷點設置/刪除 Command + \
斷點失效/生效 Command + Y
控制臺顯示/隱藏 Command + Shift + Y
光標切換到控制臺 Command + Shift + C
清空控制臺內容 Command + K

實戰


說了這么多, 終于到了實戰的時候了, 有人說, 為什么一個 LLDB 操作還要實戰呢? 原因在于.... 這里面真的好多坑啊, 沒有想象中的那么簡單.

暫時先寫這么多, 未完待續


Lemon龍說:

如果您在文章中看到了錯誤 或 誤導大家的地方, 請您幫我指出, 我會盡快更改

如果您有什么疑問或者不懂的地方, 請留言給我, 我會盡快回復您

如果您覺得本文對您有所幫助, 您的喜歡是對我最大的鼓勵

如果您有好的文章, 可以投稿給我, 讓更多的 iOS Developer 在簡書這個平臺能夠更快速的成長


最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 轉載 與調試器共舞 - LLDB 的華爾茲: https://objccn.io/issue-19-2/ 推薦:i...
    F麥子閱讀 3,357評論 0 10
  • LLDB的Xcode默認的調試器,它與LLVM編譯器一起,帶給我們更豐富的流程控制和數據檢測的調試功能。平時用Xc...
    CoderSC閱讀 1,386評論 0 2
  • [轉]淺談LLDB調試器文章來源于:http://www.cocoachina.com/ios/20150126/...
    loveobjc閱讀 2,586評論 2 6
  • LLDB的Xcode默認的調試器,它與LLVM編譯器一起,帶給我們更豐富的流程控制和數據檢測的調試功能。平時用Xc...
    小笨狼閱讀 20,600評論 31 186
  • 寶寶很無聊,因為天很熱,媽媽不再身邊,吃不到喜歡的稀飯咸菜。 姑娘我很無聊,因為我是條老狗,看不到喜歡的書,下不到...
    紙字吹閱讀 346評論 0 0