前言
相关文章
相关代码
前面一篇文章,让大家了解了一些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,我会定期做一些技术分享!