真实的例子:用bar指令查看swift返回的字符串
在Objective-C环境中判断一个swift对象的返回值是非常困难的, 但仍然是可行的.
你将要做另一个例子.你还记得Person
类的fullName
方法吗?我想让你仅在fullName
方法返回Ray Wenderlich
时代码才停止执行. 那么让我们来看一下如何用bar
命令来实现它吧.
但是在你去改变我们的dlopen
符号断点之前.你需要先运行一下.你看, swift里的指针与你之前所了解的不一样, 在使用swift字符串的时候更像是在使用char * char * or unichar
或者unichar *
, 这取决于环境.
通过在断点标志上上单击来暂时禁用dlopen
符号断点.
打印Person.swift
文件的前16行代码.在LLDB中输入一下内容:
(lldb) source list -f Person.swift -c 16
你会得到下面的输出:
看一下
fullName
, 你会发现它是一个只有getter
方法的计算型函数.
这就意味着你可以在
swift
的属性后面调用getter
语法. 在LLDB中输入下面内容:
(lldb) rb fullName.getter
你将会触发两个断点: 一个Objective-C bridge的断点, 一个实际的Swift方法断点.在工作台上点击触发断点.在我这里, 我将会在Advanced Debugging & Reverse Engineering
工作台上点击, 因为它与我感兴趣的内容有关:
当
fullName
的getter方法被调用的时候就会触发断点, 代码停止执行.
在LLDB中跳出这个函数:
(lldb) finish
检查返回的值.如果是64位的机器, 那就检查RAX
. 如果是AARCH64
, 那就检查X0
.当有疑问的时候, 那就尽管在Objective-C 环境中po
寄存器里的值, 看会发生什么.
(lldb) expression -lobjc -O -- $rax
你将会得到一些随机数:
105553116755808
再说一次, swift字符串的基地值可以被看作是C语言里的char *
或者 unichar *
.但是会稍微更复杂一点, 你在下一章中就会学到.
用char *
做开头. 把它转化为C语言的char *
看一下是否有效:
(lldb) expression -lobjc -O -- (char *)$rax
你会得到下面的结果:
"D"
这里发生了一些让人疑惑的事. 检查RAX
寄存器指向的内存地址:
(lldb) memory read $rax
你将会得到一些类似下面的输出:
0x600000077760: 44 00 65 00 72 00 65 00 6b 00 20 00 53 00 65 00
0x600000077770: 6c 00 61 00 6e 00 64 00 65 00 72 00 01 00 00 00
D.e.r.e.k. .S.e.l.a.n.d.e.r.....
啊.....!这不是char *
, 而是unichar *
!那些0x00s
意味着他们是Unichar
, 但是如果强转成Unichar
, 它们就会认为字符串已经结束了.在LLDB中将它们转化成合适的类型:
(lldb) expression -lobjc -O -- (unichar *)$rax
你将会得到类似下面的输出:
u"Derek Selander\x01"
这就意味着你可以用一个NSString
的API将Unichar
转化到一个字符串里, 然后与适当的对象做一个比较.
也就是说你将会用到下面这个可爱的API...
- (instancetype)initWithBytes:(const void *)bytes
length:(NSUInteger)len
encoding:(NSStringEncoding)encoding;
你有了起始地址(在寄存器里).你需要弄清楚它的长度和编码类型.长度可以用LLDB来检测, 输入下面的内容:
po strlen("Derek Selander") * 2
你正在获取字符串的长度并且将长度扩大了两倍, 因为UTF16字符串占用了双倍的字节.你在输出中得到的结果应该是28
.
既然已经到这里了, 那就看一下Ray Wenderlich
的名字的长度吧:
po strlen("Ray Wenderlich") * 2
太酷了, 这两次测试的结果都是28.
现在来处理一下编码问题...
打开一个新的终端窗口并输入下列内容:
open -h NSString.h
这将会弹出NSString
的头文件:
找到代表
NSUTF16LittleEndianStringEncoding
的数字, 这个枚举的数值将会是你所需的编码类型的正确数值.
看起来你需要的正确数值是
0x94000100
.
现在你所需要的东西都已经具备了. 回到LLDB窗口中输入下面的内容:
(lldb) e -lobjc -O -- [[NSString alloc] initWithBytes:$rax length:28
encoding:0x94000100]
哇!生效了!现在你可以运行你的NSString的查询语句了.
你已经得到了创建action
所需要的信息.回到dlopen
符号断点里, 然后输入下面这个完整的表达式.
bar fullName.getter -c '[[[NSString alloc] initWithBytes:obj length:28
encoding:0x94000100] containsString:@"Ray Wenderlich"]'
确保你的输入没有错字, 不然你就悲剧了.现在重新启用这个断点.
构建, 并运行, 在随机事件上点击确保代码的执行没有停止.
选择Server Side Swift with Perfect
然后观察会发生什么.
如果你的断点没有问题, 控制流应该会停在包含Ray Wenderlich
的fullName
的getter方法处.
接下来学什么?
接下来要学的东西会非常的刺激, 但是现在你已经学会了如何在你自己的Python中嵌入选项.
读完这章可能你已经没有多少精力了, 但是你应该回顾一下bar
指令.在调试时, 很多时候,我都想知道某个有趣的对象的栈桢情况.