引子:编译原理
程序到可执行文件需要经过四个步骤:预处理,编译,汇编和链接。
编译过程
- 词法分析,将代码的字符序列分割成一系列的词法单元(token);
- 语法分析,生成抽象语法树(AST); 3. 语义分析,检查表达式是否合法有效;
- 源代码优化器,用于生成与机器无关的中间代码表示(IR);
- 最后是代码生成器和目标代码优化器,用于生成汇编代码以及做一些优化处理。
静态语义和动态语义
- 编译过程中所能分析的语义是静态语义,是在编译期间就已经可以确定的语义,比如除数不能为 0;
- 相对的动态语义则是在运行时才能确定的语义。
静态语义问题集中显示在 Issue navigator
的 Buildtime
一栏中,如我们经常遇到的错误、警告、静态分析问题以及单元测试失败信息:
那么程序运行时的动态语义分析问题我们又该如何定位和排查呢?
Runtime Checking: 运行时检查工具
01.Memory Graph: 实战解决闭包引用循环问题
测试环境要求:Xcode 8.0 beta 及以上
闭包循环引用后,选择这个按钮:
这个时候就进入了断点模式,可以查看 issue 面板,注意选择右边 Runtime:
有很多叹号说明就有问题了。看内存中 object 的名字,有一条是 Closure captures leaked。展开后点击就可以看到这个 issue 对应的内存图形展示在中间的面板中。
当然了,我们更多的时候是在debug页面下查看:
有了这个图就很容易看出来了:myView保持了action,action保持了testMethod,testMethod中因为设置了vc的label所以也保持了VC。所以我们可以确定:方法中隐式的self的捕捉策略是strong。这样直接把方法传入子view中会引起引用循环。
02.Thread Sanitizer:线程消除器
继 Address Sanitizer
之后,Xcode8 中加入 Thread Sanitizer
用于检查一些线程问题,并会显示在 runtime issue
列表中,使用 Thread Sanitizer
需如下图所示开启,重新编译运行后即可。
Thread Sanitizer
主要检测以下问题:
- 使用了未初始化的
mutexes
- Thread leaks,如缺少
pthread_join
- 在 signal handlers 中的不安全调用,如调用
malloc
- 在错误的线程中做 unlock
- 最重要的一点:
Data races
问题
凡是通过 Thread Sanitizer
检查出来问题,都代表着有很严重的隐患,那么,赶快修复吧!
03.循环布局检测
当你进入某个界面或点击某个按钮后,发现屏幕不再响应事件,然后进入无限等待,debug下可以看到CPU满负荷,RAM也不断增加,那么有可能就进入了循环布局状态。
可在 Launch Arguments
中添加 -UIViewLayoutFeedbackLoopDebuggingThreshold
,设置循环布局阀值,如100,然后当循环布局次数超过阀值时会抛出异常,此时通过 po [_UIViewLayoutFeedbackLoopDebugger layoutFeedbackLoopDebugger]
可输出详细信息。
04.Register read:读取注册信息
平时是否有过crash在没有源码的第三方库或系统库中,而检查自己代码又没有发现任何问题?那么试试 register
命令吧,可能会有意向不到的效果。
通过 register read
可以读取当前状态下寄存器存储的变量,在没有源码的情况下运气好就可以获取到一些非常有用的信息,毕竟很有可能是你传入的某个错误值导致的最终 crash,而这个值就可能从寄存器中取到,从而快速定位原因。
register
还有一个非常有用的特性,当刚进入某一函数时,可通过 register read $arg1 $arg2 ...
读取函数的各个参数值。
05.本地化检查
首先需要如下方式开启,Static Analysis
将会检查 UI元素
的文字设置是否使用了 NSLocalizedString
。