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,我会定期做一些技术分享!

 

原创文章 88 获赞 21 访问量 2万+

猜你喜欢

转载自blog.csdn.net/ZhaiAlan/article/details/105064246
今日推荐