前言
相關文章
相關代碼
前面一篇文章,讓大家瞭解了一些LLDB 在斷點上的用法,這篇文章主要對lldb的一些進階用法。
LLDB進階
在之前的iOS底層探索中,我經常 Xcode
調試的時候在 LLDB
上輸入:p xxx
或者 輸入了po xxx
,就獲取了一個對象的值。那麼 p
或者 po
含義到底是什麼呢?
LLDB
輸入help p
和 help po
,查看說明如下:
p
是expression
的簡寫。LLDB
上輸入的p xxx
爲 執行 xxxpo
是expression -O
的縮寫。- 再輸入
help expression
,po
: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 1
、frame select 3
直接跳轉相應的隊棧。這裏我們先看下
可以看到當前在執行的方法爲lldb2方法,想要回到上一個方法 LLDB
輸入 up
,去下一個 down
。
如果想查看 lldbText2
中的參數可以使用 frame variable
,這也一定程度告訴了我們,方法中的 self
不一定是我們當前的 ViewController
。self實際是objc_msgSend中的隱士參數,實際就是上一個調用你的是誰來決定的;
更改一下上方代碼,將 lldb1
、lldb2
、lldb3
、lldb4
都添加上參數 str
,並且給 lldb2
設置一個斷點。
運行後點擊屏幕,進入斷點 lldb2
,如果我們使用 up
指令進入了 lldb1
然後修改了 str
,lldb2
還會再輸出一次嗎。
lldb4
輸出的結果是什麼?
lldb2 方法不會在執行了,因爲我們 lldb2
方法已經走過了,並且lldb4中的輸出結果已經執行過了,也就是說過去的時間我們無法修改一樣。
那有沒有那種方式能夠修改這些呢:
我們可以使用expression 關鍵詞
還有個斷點關鍵詞 thread return
,但是這個方法執行過後進入了 lldb2
,修改了 lldb2
中的 str
後過掉斷點
並沒有打印出 lldb4
,其實 thread return
執行後,給當前的方法後面添加了一個 return
,所以就不會往下繼續執行了。
lldb流程控制指令
$continue c
- 單步運行,將子函數當做整體一步執行
$n next
,彙編之下使用ni
- 單步運行,遇到子函數會進去
$s
,彙編之下使用si
lldb其他指令
image list 查看關聯macho文建
x 讀取地址
register read 查看寄存器
stop-hook
讓你在每次stop
(斷點)的時候去執行一些命令,只針對breadpoint
,watchpoint
總結:
這篇文章寫的比較少,主要總結了一下,開發過程中LLDB 中的一點實用的操作,修改參數傳值,更改數據源等,希望對大家有用,歡迎大家點贊,關注我的CSDN,我會定期做一些技術分享!