Instruments

记录下学习的要点,内容来自Practical Instruments

Time Profier

Time Profiler分析原理:它按照固定的时间间隔来跟踪每一个线程的堆栈信息,通过统计比较时间间隔之间的堆栈状态,来推算某个方法执行了多久,并获得一个近似值。其实从根本上来说与我们的原始分析方法异曲同工,只不过其将各个方法消耗的时间统计起来。

一些使用的快捷键介绍:

  • Option+滚动 放大或者缩小区域
  • Option+左键 快速的展开

推荐视频

  • Using Time Profiler in Instruments

Call Tree选项:

  • Separate by Thread: 每个线程应该分开考虑。只有这样你才能揪出那些大量占用CPU的”重”线程
  • Invert Call Tree: 从上倒下跟踪堆栈,这意味着你看到的表中的方法,将已从第0帧开始取样,这通常你是想要的,只有这样你才能看到CPU中话费时间最深的方法.也就是说FuncA{FunB{FunC}} 勾选此项后堆栈以C->B-A 把调用层级最深的C显示在最外面
  • Hide System Libraries: 勾选此项你会显示你app的代码,这是非常有用的. 因为通常你只关心cpu花在自己代码上的时间不是系统上的
  • Flatten Recursion: 递归函数, 每个堆栈跟踪一个条目
  • Top Functions: 一个函数花费的时间直接在该函数中的总和,以及在函数调用该函数所花费的时间的总时间。因此,如果函数A调用B,那么A的时间报告在A花费的时间加上B花费的时间,这非常真有用,因为它可以让你每次下到调用堆栈时挑最大的时间数字,归零在你最耗时的方法。

图片加载的性能问题

可参考:

  • 谈谈 iOS 中图片的解压缩
  • Advanced Graphics and Animations for iOS Apps

Time Profier中,可发现图片显示的解码都是在主线程的

如果有大量的图片需要解码,则会耗费大量的时间,带来不好的用户体验,所以可以把图片的解码放在后台中
如下的AsyncImageView

import UIKitclass AsyncImageView: UIView {private var _image: UIImage?var image: UIImage? {get {return _image}set {_image = newValuelayer.contents = nilguard let image = newValue else { return }DispatchQueue.global(qos: .userInitiated).async {let decodedImage = self.decodedImage(image)DispatchQueue.main.async {self.layer.contents = decodedImage?.cgImage}}}}func decodedImage(_ image: UIImage) -> UIImage? {guard let newImage = image.cgImage else { return nil }let cachedImage = AsyncImageView.globalCache.object(forKey: image)if let cachedImage = cachedImage as? UIImage {return cachedImage}let colorSpace = CGColorSpaceCreateDeviceRGB()let context = CGContext(data: nil, width: newImage.width, height: newImage.height, bitsPerComponent: 8, bytesPerRow: newImage.width * 4, space: colorSpace, bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue)context?.draw(newImage, in: CGRect(x: 0, y: 0, width: newImage.width, height: newImage.height))let drawnImage = context?.makeImage()if let drawnImage = drawnImage {let decodedImage = UIImage(cgImage: drawnImage)AsyncImageView.globalCache.setObject(decodedImage, forKey: image)return decodedImage}return nil}
}extension AsyncImageView {struct Static {static var cache = NSCache<AnyObject, AnyObject>()}class var globalCache: NSCache<AnyObject, AnyObject> {get { return Static.cache }set { Static.cache = newValue }}}

优化启动

两种启动

  • 冷启动(Cold Launches):当app长时间没有被启动或者device被rebooted
  • 热启动(Warm Launches):是指app需要的一些dylibs任然存在于device的disk缓存中

可参考iOS 启动时优化

main()方法运行之前,发生的事情:

  • dylib loading
  • rebase/binding
  • Obj-C Setup
  • Initializers

可参考优化 App 的启动时间的一些解释

冷启动(Cold launch)耗时才是我们需要测量的重要数据,为了准确测量冷启动耗时,测量前需要重启设备。在 main() 方法执行前测量是很难的,好在 dyld 提供了内建的测量方法:在 XcodeEdit scheme -> Run -> Auguments 将环境变量 DYLD_PRINT_STATISTICS 设为 YES,可以输出main()方法被调用之前耗费了多少时间:

控制台输出的内容大概如下,注意运行前重启测试设备:

Total pre-main time: 421.28 milliseconds (100.0%)dylib loading time: 252.24 milliseconds (59.8%)rebase/binding time:  31.76 milliseconds (7.5%)ObjC setup time:  25.56 milliseconds (6.0%)initializer time: 111.63 milliseconds (26.4%)slowest intializers :libSystem.B.dylib :   4.73 milliseconds (1.1%)AFNetworking :  14.82 milliseconds (3.5%)AsyncDisplayKit :  77.89 milliseconds (18.4%)Catstagram :  43.14 milliseconds (10.2%)

可发现大部分的时间都花费在了dylib loading。这个测试的工程项目,使用CocoaPods加载了许多动态库,如AFNetworkingAsyncDisplayKit等,如下:

现在再热启动一下,输出结果如下:

Total pre-main time: 241.62 milliseconds (100.0%)dylib loading time: 149.82 milliseconds (62.0%)rebase/binding time:   7.30 milliseconds (3.0%)ObjC setup time:  12.63 milliseconds (5.2%)initializer time:  71.44 milliseconds (29.5%)slowest intializers :libSystem.B.dylib :   2.44 milliseconds (1.0%)AFNetworking :   7.52 milliseconds (3.1%)AsyncDisplayKit :  55.79 milliseconds (23.0%)Catstagram :  24.86 milliseconds (10.2%)

现在使用静态库试一下,注释掉Podfile中的use_frameworks!pod install之后,如下:

重启设备,再运行,冷启动,输出如下:

Total pre-main time: 361.91 milliseconds (100.0%)dylib loading time: 222.27 milliseconds (61.4%)rebase/binding time:  32.18 milliseconds (8.8%)ObjC setup time:  11.53 milliseconds (3.1%)initializer time:  95.84 milliseconds (26.4%)slowest intializers :libSystem.B.dylib :   4.71 milliseconds (1.3%)Catstagram : 157.16 milliseconds (43.4%)

热启动,输出入如下:

Total pre-main time: 194.98 milliseconds (100.0%)dylib loading time: 121.41 milliseconds (62.2%)rebase/binding time:   1.88 milliseconds (0.9%)ObjC setup time:   6.39 milliseconds (3.2%)initializer time:  65.22 milliseconds (33.4%)slowest intializers :libSystem.B.dylib :   2.40 milliseconds (1.2%)Catstagram : 111.07 milliseconds (56.9%)

静态库和动态库的区别,参考OS静态库 【.a 和framework】【超详细】

静态库: 链接时完整地拷贝至可执行文件中,被多次使用就有多份冗余拷贝。
动态库: 链接时不复制,程序运行时由系统动态加载到内存,供程序调用,系统只加载一次,多个程序共用,节省内存。[ios暂时只允许使用系统动态库];
静态库和动态库是相对编译期和运行期的:静态库在程序编译时会被链接到目标代码中,程序运行时将不再需要改静态库;而动态库在程序编译时并不会被链接到目标代码中,只是在程序运行时才被载入,因为在程序运行期间还需要动态库的存在。
总结:同一个静态库在不同程序中使用时,每一个程序中都得导入一次,打包时也被打包进去,形成一个程序。而动态库在不同程序中,打包时并没有被打包进去,只在程序运行使用时,才链接载入(如系统的框架如UIKit、Foundation等),所以程序体积会小很多,但是苹果不让使用自己的动态库,否则审核就无法通过。

main()方法之后加载过程

  • UIApplicationMain()
  • application(_:willFinishLaunchingWithOptions:)
  • didFinishLaunchingWithOptions(_:)

当第一个CATransaction is committed,system会认为加载完成

使用Instruments中的Time Profier,来检测下:

会发现启动时间达到3.11s,而willFinishLaunchingWithOptions其中的CoolLogger.reportLogs()方法,耗时1.2s之久,需要优化,如下:

        DispatchQueue.global(qos: .background).async {CoolLogger.reportLogs()}

改进之后,启动时间为106.18ms,如下:

内存相关

主要用来检测是否有循环引用、内存泄露、僵尸对象等。
可使用的工具:

  • Instruments中的Allocations、Leaks
  • Xcode的Memory Graph Debug Tool

参考iOS 性能优化:Instruments 工具的救命三招

Memory Graph Debug Tool

如下图示:

Core Animation

Core Animation可以用来优化显示

这里主要讲了2个问题,Alpha BlendingOffscreen Rendering(离屏渲染)
其实这些都可以在模拟器中的Debug选项下来调试,可参考浅谈iOS中的视图优化

Alpha Blending

很多情况下,界面都是会出现多个UI控件叠加的情况,如果有透明或者半透明的控件,那么GPU会去计算这些这些layer最终的显示的颜色,也就是我们肉眼所看到的效果

Core AnimationDebug Options中勾选Color Blended Layers,如下:

在Xcode9中,Debug Options就没有了,参考Xcode9.3之后使用Instruments 调试 Core Animation

就这个示例的项目来说,选中Color Blended Layers后,红色显示的表示就是有图层混合的问题,如下,是未优化之前的显示效果:

这里出现问题的原因是,创建label的时候,没有指定backgroundColor,其默认就是clear color,所以会有图层混合的问题。因此简单的解决办法就是为label指定backgroundColor,这里简单的指定一个white就行,跳转后,显示效果如下:

离屏渲染

关于离屏渲染不错的文章离屏渲染优化详解:实例示范+性能测试

官方公开的的资料里关于离屏渲染的信息最早是在 2011年的 WWDC, 在多个 session 里都提到了尽量避免会触发离屏渲染的效果,包括:mask, shadow, group opacity, edge antialiasing。

教程的这里例子中,使用了shadow,所以触发了离屏渲染,使用Core Animation工具,勾选Color Offscreen-Renderd Yellow,显示效果如下,黄色区域表示使用了离屏渲染:

这里使用NSShadow来代替原来view.layer上的shadow设置:

        for view in [photoDescriptionLabel, userNameLabel] {/*view.layer.shadowColor = UIColor.lightGray.cgColorview.layer.shadowOffset = CGSize(width: 0.0, height: 5.0)view.layer.shadowOpacity = 1.0view.layer.shadowRadius = 5.0*/let shadow = NSShadow();shadow.shadowColor = UIColor.lightGrayshadow.shadowOffset = CGSize(width: 0.0, height: 5.0)shadow.shadowBlurRadius = 5.0if let mutableAttributedString = view.attributedText as? NSMutableAttributedString {let range = NSRange(location: 0, length: mutableAttributedString.string.characters.count)mutableAttributedString.addAttribute(NSShadowAttributeName, value: shadow, range: range)}}

调整后的结果如下:

圆角

在上面的例子中,还有一个问题,就是头像任然存在离屏渲染的问题,如何解决?
还是通过例子来说明:
1.如下,通过设置layercornerRadius和设置clipsToBounds来说设置圆角

class RoundView: UIImageView {override init(frame: CGRect) {super.init(frame: frame)self.layer.cornerRadius = 100.0self.clipsToBounds = trueself.image = UIImage(named: "profPic")!self.contentMode = .scaleAspectFill}required init?(coder aDecoder: NSCoder) {fatalError("init(coder:) has not been implemented")}}

通过Core Animation中的Color Offscreen-Renderd Yellow来查看,结果如下,显示会有离屏渲染的问题:

2.第二方式是,自己绘制,通过贝塞尔曲线来裁剪圆角,主要是在draw(_:)中处理:

    override func draw(_ rect: CGRect) {let ctx = UIGraphicsGetCurrentContext()let bezierPath = UIBezierPath(roundedRect: rect, cornerRadius: 100)bezierPath.addClip()ctx?.addPath(bezierPath.cgPath)image?.draw(in: rect)}

此时,显示圆角周围是黑色的,如下:

设置isOpaque = false,就可以正常显示了:

但是这是又会出现另一个问题,图层混合,在Core Animation中选中选中Color Blended Layers,如下:

3.第三种方式,技巧就是在image之上渲染不透明的圆角

class RoundView: UIView {var image = UIImage()override init(frame: CGRect) {super.init(frame: frame)image = UIImage(named: "profPic")!contentMode = .scaleAspectFillroundifyCorners()}required init?(coder aDecoder: NSCoder) {fatalError("init(coder:) has not been implemented")}override func draw(_ rect: CGRect) {image.draw(in: rect)}func roundifyCorners() {let radius: CGFloat = 100.0let path = UIBezierPath(roundedRect: bounds, cornerRadius: 0)let circlePath = UIBezierPath(roundedRect: bounds, cornerRadius: radius)path.append(circlePath)let fillLayer = CAShapeLayer()fillLayer.path = path.cgPathfillLayer.fillRule = kCAFillRuleEvenOddfillLayer.fillColor = UIColor.orange.cgColorlayer.addSublayer(fillLayer)}
}

显示,效果如下,你会看到橙色pre-composited的圆角和一个绿色,non-alpha混合图像

可参考的文章:

  • Instruments性能优化-Core Animation
  • 离屏渲染
  • 浅谈iOS中的视图优化
  • 离屏渲染优化详解:实例示范+性能测试
  • iOS 保持界面流畅的技巧
  • iOS-离屏渲染详解.

Energy

影响电量的主要因素:

  • Networking and I/O
  • Timers
  • Location Services
  • Motion

1.在Xcode中的show the debug navigator中的Energy Impact中查看电量

2.通过Instruments中的Energy Log模板,并结合Time Profiler一起使用,来查看影响电量的因素

Instruments相关推荐

  1. iOS性能优化:Instruments使用实战

    最近采用Instruments 来分析整个应用程序的性能.发现很多有意思的点,以及性能优化和一些分析性能消耗的技巧,小结如下. Instruments使用技巧 关于Instruments官方有一个很有 ...

  2. Instruments of Xcode

    Instruments of Xcode Xcode仪表是一个性能分析.测试工具,动态跟踪和剖析OS X和iOS代码.灵活有力的跟踪一个过程,收集数据,检查收集回来的数据.通过这种方式,仪器帮助你理解 ...

  3. 使用Xcode和Instruments调试解决iOS内存泄露(转)

    转自:http://blog.csdn.net/totogo2010/article/details/8233565 虽然iOS 5.0版本之后加入了ARC机制,由于相互引用关系比较复杂时,内存泄露还 ...

  4. Error: Command failed: xcrun instruments -s

    Error: Command failed: xcrun instruments -s xcrun: error: unable to find utility "instruments&q ...

  5. iOS 使用Instruments优化内存性能

    iOS 使用Instruments优化内存性能 问题 项目中使用到图片合成视频,发现内存增长十分的迅速,导致一些因为内存引起的问题,本文使用这个案例,结合Instruments工具检测和分析问题,最终 ...

  6. IOS-React-Native:unable to find utility instruments, not a developer tool or in PATH

    解决办法: 终端运行命令 sudo xcode-select -s /Applications/Xcode.app/Contents/Developer/ 错误信息: Found Xcode proj ...

  7. About Instruments

    About Instruments Instruments是一个强大的和灵活的性能分析和测试工具的Xcode工具集的一部分.它的目的是帮助您配置您的OS X和iOS应用程序.进程和设备,以便更好地理解 ...

  8. iOS系类教程之用instruments来检验你的app

    2019独角兽企业重金招聘Python工程师标准>>> 发布于:2014-01-14 10:23阅读数:22668 比较了好多关于instruments 还是发现老外写的比较牛逼.于 ...

  9. 转iOS性能优化:Instruments使用实战

    最近采用Instruments 来分析整个应用程序的性能.发现很多有意思的点,以及性能优化和一些分析性能消耗的技巧,小结如下. Instruments使用技巧 关于Instruments官方有一个很有 ...

  10. Instruments模板介绍(更新中...)

    第一章.Instruments(仪器)快速入门 Instruments可以用来收集关于一个或者多个系统进程的性能和行为的数据,并跟踪睡着时间产生的数据.它提供了好几个分析模板: Blank 任何类型都 ...

最新文章

  1. CVPR 2022|MPViT:用于密集预测的多路径视觉Transformer
  2. CVPR2020人脸防伪检测挑战赛冠亚军论文解读(下篇)
  3. 再次修订后的版本。。。。。。1.0(发布版,射线求交三角形)
  4. Leetcode 383 Ransom Note
  5. Go语言web框架beego:目录说明
  6. AJAX——AJAX请求递归
  7. ospf协议_三级网络技术考前选择题3—OSPF协议
  8. FoveaBox:目标检测新纪元,无Anchor时代来临!
  9. typora用什么文档管理_会展经济与管理专业自考本科毕业后有什么用
  10. Lesson 04:类和对象,类的成员变量、成员方法、构造方法
  11. TensorFlow(二) 用TensorFlow为线性回归算法实现矩阵分解
  12. 【strtok()】——分割字符串
  13. 在Win7中IIS配置Asp.Net虚拟文件夹的方法及错误总结!
  14. Java-斗地主小游戏洗牌发牌(控制台程序)
  15. ora01017 linux,ORA-01017: invalid username/password; logon denied 解决办法
  16. python alpha通道_python – 使用matplotlib和alpha通道组合图片和绘图
  17. 计算机显卡故障,电脑显卡有什么故障 电脑显卡常见故障汇总
  18. Graph Stacked Hourglass Networks for 3D Human Pose Estimation
  19. BeanUtils.populate 的使用
  20. js中数组的entries方法

热门文章

  1. 如何选择Java培训机构
  2. 马云的经典语录(转)
  3. 2007年开关稳压电源设计报告!!!
  4. zigzag算法详解
  5. c语言x的n次方怎么写_C语言入门教程(三)进制与操作符
  6. 为人处事的19个技巧
  7. git+小乌龟安装教程。。
  8. 信息化项目验收确认测试的内容和流程有哪些?
  9. Windows查看进程命令
  10. 小米电视4显示android,全面分析小米电视盒子好用吗?小米电视盒子4体验评测分享...