Xcode提供了用于以图形化和数字化形式探索应用内部行为的工具,你应该了解这些工具的使用方式。可以通过调试导航器中的仪表盘在应用运行时监控其关键指标,如CPU与内存使用。Instruments则是个复杂且强大的辅助应用,它会收集有助于追踪问题的分析数据,并提供你所需要的数字化信息来改进应用的性能与响应性。在应用开发趋于结束之际,你应该花些时间了解一下Instruments的用法(众所周知,过早的优化是万恶之源)。
9.9.1 仪表盘
调试导航器中的仪表盘会在应用构建和运行时起作用。单击某一项可以在编辑器中查看到进一步的细节信息。仪表盘并未提供非常详尽的信息,不过它却非常轻量级且总是处于激活状态,因此可以通过它在任何时候了解到应用的总体运行情况。特别地,如果出现了问题,比如,长时间的高CPU使用率或内存使用不断飙升,那就可以在仪表盘中定位到,然后通过Instruments找出问题所在。
有4种基本的仪表盘:CPU、内存、磁盘与网络。根据环境的不同,你可能还会看到其他仪表盘。比如,在Xcode 7中,当在设备上运行时,电量仪表盘会出现;对于某些设备来说还可能会出现GPU仪表盘。如果应用使用了iCloud,那还会看到iCloud仪表盘。
在图9-12中,我频繁使用了应用一段时间,不断重复地执行用户可能会操作的最消耗内存的动作。这些动作会导致内存使用量攀升,不过应用的内存使用量最后会趋于平稳,因此我认为应用在内存使用量上是没问题的。
图9-12:调试导航仪表盘
值得注意的是,图9-12是在设备上运行的结果。在模拟器上运行则会得到完全不同的结果,这个结果是不正确的。
9.9.2 Instruments
可以在模拟器或设备上使用Instruments。在设备上进行的是最终的测试,目的是得到尽可能准确的结果。
要使用Instruments,请在项目窗口工具栏的方案弹出菜单中设置所需的目标,然后选择Product→Profile。这样应用就会使用方案下的Profile动作进行构建;在默认情况下,这会使用发布构建配置,这可能就是你所期望的。如果在设备上运行,那么你可能会看到一些验证警告,不过可以忽略它们。Instruments会启动;如果方案的Instrument弹出菜单将Profile动作设为了Ask on Launch(默认值),那么Instruments就会弹出一个对话框,你可以从中选择追踪模板。
此外,还可以单击调试导航器仪表盘编辑器中的Profile In Instruments;如果仪表盘发现了问题,你又想通过更为详尽的Instruments监控来重现问题,这么做就是很方便的。Instruments启动后,选择合适的追踪模板。对话框会给出两个选择:Restart会先停止应用,然后使用Instruments再次启动;Transfer则会保持应用的运行,并将Instruments挂接到应用中。
当Instruments的主窗口出现时,可以进一步对其定制来分析感兴趣的数据,可以将Instruments窗口的结构保存为自定义模板。需要单击Record按钮,或选择File→Record Trace来运行应用。现在,可以像用户那样与应用交互了,而Instruments则会记录下统计数据。
如果之前将发布配置的代码签名身份构建设置为iOS Distribution来归档或分发应用(本章后面将会介绍),那就无法在设备上使用Instruments进行分析了。必须要将该构建设置为iOS Developer。
Instruments的使用是个高级主题,这超出了本书的讨论范围。事实上,仅是介绍Instruments本身就可以写一本书。要想了解进一步的信息,请参考Apple的文档,特别是Instruments User Reference与Instruments User Guide。此外,往年的很多WWDC都有关于Instruments的视频介绍;请查找名字中包含“Instruments”或“Performance”的资料。这里仅仅简单介绍一下Instruments到底能做什么。
图9-13展示了在Instruments中能够完成与图9-12调试导航器仪表盘所能完成的相同事情。我将目标设定为我的设备。选择Product→Profile;当Instruments启动后,我选择Allocations追踪模板。当应用在Instruments下运行时,我使用了一会儿,然后暂停了Instruments,与此同时它会绘制应用的内存使用情况表。查看这张表,我发现内存使用量最高达到了10MB,不过大部分时间,内存使用量都处在一个较低的水平上(不到4MB)。我对这个结果感到很满意。
图9-13:Instruments以图形化形式展示了一段时间内的内存使用
Instruments的另一个强大之处就是检测内存泄漏的能力。在图9-14中,我运行了第5章的保持循环代码:有一个Dog类实例和一个Cat类实例,它们彼此间都引用了对方。没有其他引用再指向这两个实例了,因此它们都存在泄漏问题。我通过Leaks追踪模板来分析应用。Instruments检测到了泄漏,甚至还绘制了图表展示了错误的结构!
图9-14:Instruments展示了保持循环
在最后这个示例中,我想知道是否可以缩短Diabelli’s Theme应用加载图片的时间。我将目标设为了设备,因为只有真正的设备才能体会到速度的重要性并且需要进行度量。选择Product→Profile。Instruments启动,我选择了Time Profiler追踪模板。当应用在设备上随Instruments启动后,我不断加载新图片来执行这部分代码。
在图9-15中,我已经暂停了Instruments,看看图上都有什么。打开窗口下方的小三角,我可以钻取到自己的代码,这是由模块名MomApp2所标识的(之所以叫这个名字是因为一开始是将这个应用作为我母亲的生日礼物的)。
双击这一行可以看到自己代码的执行时间(如图9-16所示)。分析器所指出的对CGImageSourceCreateThumbnailAtIndex的调用引起了我的注意;此处消耗了大部分的CPU时间。该调用位于ImageIO框架中;它并不是我写的代码,因此我对其速度的提升无能为力。不过,我可以通过另外一种方式加载图片;比如,以一些临时的内存作为代码,我可以将图片全部加载进来并缩放。如果担心速度问题,我可以花点时间做试验。关键在于我现在知道了该如何做试验。这只不过是Instruments所擅长的基于事实的数值分析的一个方面而已。