第二十章:有选项和参数的桥接脚本(四)

真实的例子:用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

你会得到下面的输出:

屏幕快照 2018-10-11 下午1.55.57.png

看一下fullName, 你会发现它是一个只有getter方法的计算型函数.
这就意味着你可以在swift的属性后面调用getter语法. 在LLDB中输入下面内容:

(lldb) rb fullName.getter

你将会触发两个断点: 一个Objective-C bridge的断点, 一个实际的Swift方法断点.在工作台上点击触发断点.在我这里, 我将会在Advanced Debugging & Reverse Engineering工作台上点击, 因为它与我感兴趣的内容有关:

屏幕快照 2018-10-11 下午2.01.25.png

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的头文件:

屏幕快照 2018-10-11 下午2.23.42.png

找到代表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 WenderlichfullName的getter方法处.

接下来学什么?

接下来要学的东西会非常的刺激, 但是现在你已经学会了如何在你自己的Python中嵌入选项.
读完这章可能你已经没有多少精力了, 但是你应该回顾一下bar指令.在调试时, 很多时候,我都想知道某个有趣的对象的栈桢情况.

你可能感兴趣的:(第二十章:有选项和参数的桥接脚本(四))