Xcode 断点调试时打印变量值报错的问题(编译优化相关)

2020-11-13  本文已影响0人  刃之剑
文章因为遇到问题:支付的时候传的值一直断点显示为空,
但是判断了为空就不走了,知道NSLog才发现有值的.
连Bool值的对象也无法直接判断为YES还是为NO,
在Debug模式下直接写YES或者NO才有效果,白费了几十大洋调试.
所以百度找原因摘抄的部分,有问题欢迎百度,而不是去开车群找司机.
在日常开发中,我们经常会在 Debug 模式下打断点进行调试,并通过 LLDB 的 `po` 命令在控制台打印一些变量的值,以方便排查问题。

今天在 Release 模式下编译运行项目,发现要打印某一变量的值时(`po xxx`),报如下错误:

error: Couldn't materialize: couldn't get the value of variable xxx: no location, value may have been optimized out
error: errored out in DoExecute, couldn't PrepareToExecuteJITExpression


大致意思是说,`xxx` 的值不存在,可能已经被编译优化了。而且在断点模式下当我们把鼠标的箭头移到某一变量上要进行快速浏览时,发现它们的值都是 `nil`。

查了一下才发现,原来这与 Xcode 工程的编译选项 `Optimization Level` 设置有关,它是指编译器的优化级别,优化后的代码效率比较高,但是可读性比较差,且编译时间更长,它有 6 个选项值如下图:

问题

LLDB中使用po打印变量时,变量为空,且会抛出如下报错信息:


error: warning: couldn’t get cmd pointer (substituting NULL): extracting data from value failed 
Couldn’t materialize: couldn’t get the value of variable now: no location, value may have been optimized out 
Errored out in Execute, couldn’t PrepareToExecuteJITExpression

解决方案:


Produc-> Scheme-> Edit Scheme -> run ,Build Configuration 改为Debug.
如果scheme已经是Debug, 则在Build Settings > Optimization Level项,将Debug的值设置为None[-O0]
设置完成后,重新运行后,LLDB中可以正常打印出变量的值。

什么是Optimization Level

下面是苹果的官方解释:

Specifies the degree to which the generated code is optimized for speed and binary size.
None[-O0]: Do not optimize. With this setting, the compiler's goal is to reduce the cost of compilation and to make debugging produce the expected results. Statements are independent: if you stop the program with a breakpoint between statements, you can then assign a new value to any variable or change the program counter to any other statement in the function and get exactly the results you would expect from the source code.
Fast[-O1]: Optimizing compilation takes somewhat more time, and a lot more memory for a large function. With this setting, the compiler tries to reduce code size and execution time, without performing any optimizations that take a great deal of compilation time. In Apple's compiler, strict aliasing, block reordering, and inter-block scheduling are disabled by default when optimizing.
Faster[-O2]: The compiler performs nearly all supported optimizations that do not involve a space-speed tradeoff. With this setting, the compiler does not perform loop unrolling or function inlining, or register renaming. As compared to the 'Fast' setting, this setting increases both compilation time and the performance of the generated code.
Fastest[-O3]: Turns on all optimizations specified by the 'Faster' setting and also turns on function inlining and register renaming options. This setting may result in a larger binary.
Fastest, Smallest[-Os]: Optimize for size. This setting enables all 'Faster' optimizations that do not typically increase code size. It also performs further optimizations designed to reduce code size.
Fastest, Aggressive Optimizations[-Ofast]: This setting enables 'Fastest' but also enables aggressive optimizations that may break strict standards compliance but should work well on well-behaved code.

简单翻译如下,英文水平有限,有错误的地方望指出:

指定被编译代码的执行速度和二进制文件大小的优化程度:

None[-O0]: 不优化。在这种设置下, 编译器的目标是降低编译消耗,保证调试时输出期望的结果。程序的语句之间是独立的:如果在程序的停在某一行的断点出,我们可以给任何变量赋新值抑或是将程序计数器指向方法中的任何一个语句,并且能得到一个和源码完全一致的运行结果。

Fast[-O1]: 大函数所需的编译时间和内存消耗都会稍微增加。在这种设置下,编译器会尝试减小代码文件的大小,减少执行时间,但并不执行需要大量编译时间的优化。在苹果的编译器中,在优化过程中,严格别名,块重排和块间的调度都会被默认禁止掉。
Faster[-O2]: 编译器执行所有不涉及时间空间交换的所有的支持的优化选项。在这种设置下,编译器不会进行循环展开、函数内联或寄存器重命名。和‘Fast[-O1]’项相比,此设置会增加编译时间和生成代码的性能。

Fastest[-O3]: 在开启‘Fast[-O1]’项支持的所有优化项的同时,开启函数内联和寄存器重命名选项。这个设置有可能会导致二进制文件变大。
Fastest, Smallest[-Os]: 优化大小。这个设置开启了‘Fast[-O1]’项中的所有不增加代码大小的优化选项,并会进一步的执行可以减小代码大小的优化。

Fastest, Aggressive Optimizations[-Ofast]: 这个设置开启了“Fastest[-O3]”中的所有优化选项,同时也开启了可能会打破严格编译标准的积极优化,但并不会影响运行良好的代码。
在Xcode中,为了方便调试,通常Debug模式默认为None[-O0],Release模式默认为Fastest, Smallest[-Os],在实际的项目中,可以根据项目的情况设置优化级别

Xcode 工程的 Optimization Level 值在 Debug 模式下默认为 None [-O0],表示编译器不会尝试优化代码,保证调试时输出期望的结果;而在 Release 模式下默认为 Fastest, Smallest[-Os],表示编译器将执行所有优化,且不会增加代码的长度,它是可执行文件占用更少内存的首选方案。

这也是为什么我们在 Release 模式下断点打印变量会报错,因为编译器已经给代码做了优化,它将不在调试时记录变量的值了。

此外,有时候遇到一些线上 Bug 但是在 Debug 调试时却无法复现,我猜有可能会跟编译优化有关,你觉得呢?欢迎留言讨论。

上一篇下一篇

猜你喜欢

热点阅读