LLDB 是 Xcode 中自带的一个调试工具,在开发的过程中使用好了这个调试工具,不仅是能力的一种提升,更是一种装逼的 神器。
一、如何进入 LLDB
通常当程序 crash 或者有断点的时候,会自动的变成 LLDB 模式。也可以手动 处理,直接点击这里:
image.png
也会变成 LLDB 模式。最终的效果是这样的:
image.png
二、使用 LLDB
2.1 expr 指令
这个指令的意思,能实时的执行代码中的代码逻辑。就像下面这样的:
image.png当点击下一步执行的时候,NSLog 打印的值是 CoderHG 而不是 Coder。这个功能想想都感觉挺不错的。
由于最近在学习 Shell 与 Python ,于是刚刚这样脑补了一下:
image.png这样是行不通的,难怪大家都说 Shell 与 Python 很强大。[偷笑5分钟,犯困一小时]
2.2 call
这个指令与 expr 类似,调用一行代码,形如这样的:
image.pngcall self.view.backgroundColor = [UIColor redColor];
2.3 打印
其实关于打印,应该所有的小伙伴都知道的。接着上面的步骤,做如下的操作:
image.png
在 LLDB 中有两个常见的打印指令 p 与 po。
- 1、p 通常用于打印基本数据类型的值。这个指令会默认生出一个临时变量,如$1,学习过 Shell 的小伙伴看到这个应该很激动。
- 2、po 打印变量的内容,如果是对象,其打印的内容由 -debugDescription 决定。
2.4 操作内存
对内存的操作,无非就是读写操作。
修改内存中的值:
memory write 内存地址 数值
如:memory write 0x7ffee685dba8 25
读取内存操作:
memory read/数量 _ 格式 _ 字节数 内存地址
或者
x/数量 _ 格式 _ 字节数 内存地址
2.4.1 格式
- x :代表16进制
- f :代表浮点数
- d :代表10进制
2.4.2 字节大小
- b :byte 代表1个字节
- h :half word 代表2个字节
- w :word 代表4个字节
- g :giant word 代表8个字节
如:
memory read/1wx 0x7ffee14a5ba8
memory read/1wd 0x7ffee14a5ba8
寓意是:读取 0x7ffee14a5ba8 中 4 个字节的内容。
示例如下:
2.5 bt
bt 返回所有的调用栈, 形如:
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = step over
* frame #0: 0x000000010758b6fd LLDBDev`-[ViewController viewDidLoad](self=0x00007fedad7057e0, _cmd="viewDidLoad") at ViewController.m:27
**中间被我干掉了很多。**
frame #34: 0x000000010758b79f LLDBDev`main(argc=1, argv=0x00007ffee8674108) at main.m:14
frame #35: 0x000000010c2d1d81 libdyld.dylib`start + 1
frame #36: 0x000000010c2d1d81 libdyld.dylib`start + 1
这个指令很强大,现在的 Xcode 在这里都显示不全了:
image.png
所以只能借助 bt 指令。
三、实战
没有实战的纸上谈兵,都是耍流氓。
在开始之前,先定义一个 Class,代码如下所示:
#import <Foundation/Foundation.h>
@interface HGObject : NSObject
/** 年龄 */
@property (nonatomic, assign) NSInteger age;
/** 身高 */
@property (nonatomic, assign) NSInteger height;
@end
#import "HGObject.h"
@implementation HGObject
@end
很简单的一个Class。
3.1 查看一个对象的 isa 指针
大家都说一个instance 对象中的 isa 的值就是当前 instance 对象的 class 对象的值,接下来求证一下。先写一段简单的代码:
Class cls = NSClassFromString(@"HGObject");
id obj = [[cls alloc] init];
NSLog(@"%@, %@", cls, obj);
image.png
运行代码发现:
- 1、cls 没有显示具体的地址值。
- 2、在 obj 中也根本没有看到 isa 这个成员变量。
看不到任何的地址显示,所以只能是借助 LLDB 调试工具,这里既是是使用简单的 p 或者 po指令都是不可以的。需要借助上面说的 操作内存 的指令。
image.png轻松搞定,上图中是不是就说明了一个 Class 对象的 instance 对象的 isa 的值就是其 class 对象本身的值呢?是的,本来就是这样的。
3.2 对象中的地址查看
简单的实现如下代码:
HGObject* obj = [[HGObject alloc] init];
obj.age = 18;
obj.height = 2;
NSLog(@"%@", obj);
在 NSLog 处打一个断点,运行代码,打开内存查看视图:
image.png
刚打开是这样的:
image.png
将 obj 的地址写入,再看下面这张图:
image.png
看到上面的内存图,发现一个规律,请看下图:
image.png
上图中的内存分布,如红框框所示,分别是 isa,_age 与 _height。为了验证其正确性,可以修改一下其中的值,看一下效果:
image.png
上图中的逻辑大概为:查找 _age 与 _height 对应的地址,然后修改其地址的值,然后刷新看内存视图。
四、说在最后的话
记录这些技巧,一方面是因为这些技巧往往都被我们所忽视,记录一下说明自己了解过。但是一旦熟练使用这些技巧,往往在开发中能减少大量的调试时间。
其实,为了装逼才是最重要的。
网友评论