Instruments
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 提供了内建的测量方法:在 Xcode
中 Edit 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加载了许多动态库,如AFNetworking
、AsyncDisplayKit
等,如下:
现在再热启动一下,输出结果如下:
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 Blending
和Offscreen Rendering
(离屏渲染)
其实这些都可以在模拟器中的Debug选项下来调试,可参考浅谈iOS中的视图优化
Alpha Blending
很多情况下,界面都是会出现多个UI控件叠加的情况,如果有透明或者半透明的控件,那么GPU
会去计算这些这些layer
最终的显示的颜色,也就是我们肉眼所看到的效果
在Core Animation
的Debug 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.如下,通过设置layer
的cornerRadius
和设置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相关推荐
- iOS性能优化:Instruments使用实战
最近采用Instruments 来分析整个应用程序的性能.发现很多有意思的点,以及性能优化和一些分析性能消耗的技巧,小结如下. Instruments使用技巧 关于Instruments官方有一个很有 ...
- Instruments of Xcode
Instruments of Xcode Xcode仪表是一个性能分析.测试工具,动态跟踪和剖析OS X和iOS代码.灵活有力的跟踪一个过程,收集数据,检查收集回来的数据.通过这种方式,仪器帮助你理解 ...
- 使用Xcode和Instruments调试解决iOS内存泄露(转)
转自:http://blog.csdn.net/totogo2010/article/details/8233565 虽然iOS 5.0版本之后加入了ARC机制,由于相互引用关系比较复杂时,内存泄露还 ...
- Error: Command failed: xcrun instruments -s
Error: Command failed: xcrun instruments -s xcrun: error: unable to find utility "instruments&q ...
- iOS 使用Instruments优化内存性能
iOS 使用Instruments优化内存性能 问题 项目中使用到图片合成视频,发现内存增长十分的迅速,导致一些因为内存引起的问题,本文使用这个案例,结合Instruments工具检测和分析问题,最终 ...
- 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 ...
- About Instruments
About Instruments Instruments是一个强大的和灵活的性能分析和测试工具的Xcode工具集的一部分.它的目的是帮助您配置您的OS X和iOS应用程序.进程和设备,以便更好地理解 ...
- iOS系类教程之用instruments来检验你的app
2019独角兽企业重金招聘Python工程师标准>>> 发布于:2014-01-14 10:23阅读数:22668 比较了好多关于instruments 还是发现老外写的比较牛逼.于 ...
- 转iOS性能优化:Instruments使用实战
最近采用Instruments 来分析整个应用程序的性能.发现很多有意思的点,以及性能优化和一些分析性能消耗的技巧,小结如下. Instruments使用技巧 关于Instruments官方有一个很有 ...
- Instruments模板介绍(更新中...)
第一章.Instruments(仪器)快速入门 Instruments可以用来收集关于一个或者多个系统进程的性能和行为的数据,并跟踪睡着时间产生的数据.它提供了好几个分析模板: Blank 任何类型都 ...
最新文章
- CVPR 2022|MPViT:用于密集预测的多路径视觉Transformer
- CVPR2020人脸防伪检测挑战赛冠亚军论文解读(下篇)
- 再次修订后的版本。。。。。。1.0(发布版,射线求交三角形)
- Leetcode 383 Ransom Note
- Go语言web框架beego:目录说明
- AJAX——AJAX请求递归
- ospf协议_三级网络技术考前选择题3—OSPF协议
- FoveaBox:目标检测新纪元,无Anchor时代来临!
- typora用什么文档管理_会展经济与管理专业自考本科毕业后有什么用
- Lesson 04:类和对象,类的成员变量、成员方法、构造方法
- TensorFlow(二) 用TensorFlow为线性回归算法实现矩阵分解
- 【strtok()】——分割字符串
- 在Win7中IIS配置Asp.Net虚拟文件夹的方法及错误总结!
- Java-斗地主小游戏洗牌发牌(控制台程序)
- ora01017 linux,ORA-01017: invalid username/password; logon denied 解决办法
- python alpha通道_python – 使用matplotlib和alpha通道组合图片和绘图
- 计算机显卡故障,电脑显卡有什么故障 电脑显卡常见故障汇总
- Graph Stacked Hourglass Networks for 3D Human Pose Estimation
- BeanUtils.populate 的使用
- js中数组的entries方法