lldb調試進階

前言

相關文章

        lldb調試命令一

相關代碼

LLDB 

前面一篇文章,讓大家瞭解了一些LLDB 在斷點上的用法,這篇文章主要對lldb的一些進階用法。

LLDB進階

在之前的iOS底層探索中,我經常 Xcode 調試的時候在 LLDB 上輸入:p xxx 或者 輸入了po xxx,就獲取了一個對象的值。那麼 p 或者 po 含義到底是什麼呢?

LLDB輸入help p 和 help po,查看說明如下:

  • p 是 expression 的簡寫。LLDB上輸入的 p xxx 爲 執行 xxx
  • po 是 expression -O的縮寫。
  • 再輸入 help expressionpo : expression --object-description 

LLDB上輸入的 po xxx 爲 執行 xxx 的 description 方法;

既然 p 是執行一個方法,那麼 LLDB 輸入:p 修改代碼並執行了; 

立刻進行嘗試一下;

新建工程給界面創建幾個按鈕,添加點擊事件如下所示頁面爲:

這個按鈕點擊事件代碼爲:

點擊按鈕,並進入LLDB,輸入: p [(UIButton *)sender setBackgroundColor:[UIColor redColor]]; 回車,過斷點看一下結果

按鈕顏色變紅了,這樣我們就可以玩更多的東西了,如在工程中新建一個Person類,給控制器添加一個成員變量;

NS_ASSUME_NONNULL_BEGIN

@interface Person : NSObject
/**
 *  title
 */
@property (copy, nonatomic) NSString *name;

/**
 * 年齡
 */
@property (assign,nonatomic) int age;

/**
 * 性別
 */
@property (assign,nonatomic) int sex;


@end

NS_ASSUME_NONNULL_END

#import "ViewController.h"

@interface ViewController ()
@property (nonatomic,strong) NSMutableArray *dataArray;
@end

在開發過程中,我們經常會因爲數據源中的數據量不夠,或者數據源中數據有錯誤,導致我們需要重新運行工程,下面我們來看一下如下操作:運行代碼,在按鈕點擊事件出打斷點做如下操作:

(lldb) po self
<ViewController: 0x7f925b703940>

(lldb) po self.dataArray
<__NSArrayM 0x6000013e0210>(

)

(lldb) p [self.dataArray addObject:[Person new]]
(lldb) po self.dataArray
<__NSArrayM 0x6000013e0210>(
<Person: 0x600001dd6880>
)

可以看出,本來沒有數據的數組中,添加了一個元素;

(lldb)  p self.dataArray[0]; //獲取第一個元素
(Person *) $1 = 0x0000600001e51b00
(lldb) p [(Person *)$1 setValue:@"抖音" forKey:@"name"]; //給獲取出來的元素賦值
(lldb)  p self.dataArray[0]; //重新拿出第一個元素
(Person *) $2 = 0x0000600001e51b00
(lldb) po $2.name  //因爲獲取元素沒有進行類型轉換導致不能識別
error: property 'name' not found on object of type 'id'
(lldb)  p (Person *)self.dataArray[0]; //類型轉換後獲取第一個元素
(Person *) $3 = 0x0000600001e51b00
(lldb) p $3.name  //拿出第一個元素中的name值
(__NSCFString *) $4 = 0x0000600001e6cba0 @"抖音"
(lldb) po $3.name  //使用po 也可以獲取出來
抖音

獲取出元素,並對元素進行相應的賦值 ,

p $3.name = @"快手";
(__NSCFString *) $6 = 0x0000600001e6cb80 @"快手"
(lldb) po $3.name
快手

接下來進行一波騷操作

//在一次命令中執行多個命令,並回車
(lldb) p Person *person = [Person new];person.name = @"王者榮耀";person.age = 6;[self.dataArray addObject:person];
(lldb) po self.dataArray
<__NSArrayM 0x60000102d410>(
<Person: 0x600001e51b00>,
<Person: 0x600001e51b60>
)

(lldb) p (Person *)self.dataArray.lastObject;
(Person *) $9 = 0x0000600001e51b60
(lldb) po $9.name
王者榮耀

(lldb) po $9.age
6

可以看到,多執行命令也能夠正常添加,並且可以正常輸出;

(lldb) p [self.dataArray removeLastObject];
(lldb) po self.dataArray
<__NSArrayM 0x60000102d410>(
<Person: 0x600001e51b00>
)

我們可以看到,使用LLDB 可以直接進行增,刪,改,查,不得不說LLDB的強大;

接下來我們來看一下LLDB 來看一下調用堆棧

- (IBAction)kuaiShouClick:(id)sender {
    NSLog(@"快手點擊");
    [self lldb2];
}
- (IBAction)wangZheClick:(id)sender {
    NSLog(@"王者點擊");
}
- (void)lldb1
{
    [self lldb2];
}
- (void)lldb2
{
    [self lldb3];
}
- (void)lldb3
{
    [self lldb4];
}
- (void)lldb4
{
    NSLog(@"%s",__func__);
}

lldb2方法內部打斷點,並執行bt操作如下圖:

既然使用使用了 bt 查看了隊棧,那也可以使用 frame select 1frame select 3 直接跳轉相應的隊棧。這裏我們先看下

可以看到當前在執行的方法爲lldb2方法,想要回到上一個方法 LLDB 輸入 up,去下一個 down

如果想查看 lldbText2 中的參數可以使用 frame variable,這也一定程度告訴了我們,方法中的 self 不一定是我們當前的 ViewController 。self實際是objc_msgSend中的隱士參數,實際就是上一個調用你的是誰來決定的;

更改一下上方代碼,將 lldb1lldb2lldb3lldb4 都添加上參數 str,並且給 lldb2 設置一個斷點。

運行後點擊屏幕,進入斷點 lldb2,如果我們使用 up 指令進入了 lldb1 然後修改了 strlldb2還會再輸出一次嗎。lldb4 輸出的結果是什麼?

lldb2 方法不會在執行了,因爲我們 lldb2 方法已經走過了,並且lldb4中的輸出結果已經執行過了,也就是說過去的時間我們無法修改一樣。

那有沒有那種方式能夠修改這些呢:

我們可以使用expression 關鍵詞

 

還有個斷點關鍵詞 thread return ,但是這個方法執行過後進入了 lldb2,修改了 lldb2 中的 str 後過掉斷點

並沒有打印出 lldb4,其實 thread return 執行後,給當前的方法後面添加了一個 return,所以就不會往下繼續執行了。

lldb流程控制指令

  1. $continue c
  2. 單步運行,將子函數當做整體一步執行 $n next,彙編之下使用 ni
  3. 單步運行,遇到子函數會進去 $s,彙編之下使用 si

lldb其他指令

  • image list      查看關聯macho文建 
  • x               讀取地址
  • register read   查看寄存器
  • stop-hook 讓你在每次stop(斷點)的時候去執行一些命令,只針對 breadpoint,watchpoint

總結:

這篇文章寫的比較少,主要總結了一下,開發過程中LLDB 中的一點實用的操作,修改參數傳值,更改數據源等,希望對大家有用,歡迎大家點贊,關注我的CSDN,我會定期做一些技術分享!

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章