Instruments 之 Energy Log

对于生活离不开手机的我们来说,手机的电量就是一条重要的生命线,一般来说,当电量低于 20% 的时候,我们的心总是那么揪着。作为一个开发者来说,我们应该为用户的手机省电,让用户有限的电量能够更长时间的使用我们开发的 APP,对用户,对我们开发者来说是两全其美的方案。所以 APP 的电量消耗也应该是性能优化的点。

案例

还是以 raywenderlich 的 Catstagram APP 作为分析案例。该案例是一个带有图片的列表。

案例截图

值的注意的是在我的开发环境下 Energy 需要运行在真机设备上,我的开发环境是 Xcode 8.3.2 , iPhone 6 (10.3.1)。

使用 Energy Impact

Energy Impact 是 Xcode 自带的一个用于查看设备电量开销概况的工具。

Energy Impact 图

如上图所示,点击 Xcode 左边的 Energy Impact 栏目就可以看到设备上正在运行的 APP 的电量消耗水平。

i指标

看图左边有 CPU ,Network , Location , GPU, Background 五个指标,这 5 个 指标也是能耗大户,右边的表格中的若是被灰色填充,那么就意味着在那个时刻,该指标是活跃的。比如图上所示 CPU 和 Network 一直都是被灰色填充,那么就意味着 CPU 和 Network 一直处于活跃状态。顶部有蓝色和红色的柱形图,红色是Overhead指标,表示除这个 APP 外,系统的其他电量消耗,蓝色是Cost,表示这个 APP 的电量消耗。关于更多的 Energy Impact 信息可以参考 Apple 的官方文档 https://developer.apple.com/library/content/documentation/Performance/Conceptual/EnergyGuide-iOS/MonitorEnergyWithXcode.html 。这里就不再累赘。

高能耗

APP 运行一段时间,滑动几次列表之后, APP 的能耗就变的非常高,从图中就可以看出,APP 在静止状态的电量高消耗情况肯定是不正常的,Energy Impact 只能看出是否有问题,而不能指出哪里可能有问题。那么这个时候就要祭出 Instruments 利器了。

Instruments 之 Energy

Command + I 运行 Instruments 选择 Energy Log 模板。

选择 Energy Log 模板
Energy Log 指标

看左边的 Energy Log 的指标有 Energy,CPU,Network等等应有尽有。

点击开始按钮,录制 APP 运行情况


APP 运行情况

从图中可以看出整个 APP 的能量消耗情况,但是存在一个问题,这个问题就是我们已经知道了APP 的这些能量消耗情况,但是怎么知道要去修改哪里的代码呢?这个时候我们需要 Time Profiler 工具。

添加 Time Profiler 工具

如上图所示,我们添加了 Time Profiler 工具用来记录 APP 在某个时间段的代码运行情况。

万事具备之后,我们重新开始录制 APP 的运行情况。

APP 的运行情况
Timer Profiler

Energy Log 结合 Timer Profiler 的使用,避免干扰我们隐藏系统库内容,显示我们的代码调用。

image.png

按照代码执行时间的权重比,找到了 CatPhotoTableViewCell 的 panImage(with yRotation: CGFloat) 方法。通过代码追溯,我们找到了 CatFeedViewController.swift 文件的 viewDidLoad() 方法,找到了 panImage(with yRotation: CGFloat) 方法被频繁调用的地方

 motionManager.startDeviceMotionUpdates(to: .main, withHandler:{ deviceMotion, error in
            guard let deviceMotion = deviceMotion else { return }
            
            self.lastY = deviceMotion.rotationRate.y
            
            let xRotationRate = CGFloat(deviceMotion.rotationRate.x)
            let yRotationRate = CGFloat(deviceMotion.rotationRate.y)
            let zRotationRate = CGFloat(deviceMotion.rotationRate.z)
            
            print("y \(yRotationRate) and x \(xRotationRate) and z\(zRotationRate)")
            
            if abs(yRotationRate) > (abs(xRotationRate) + abs(zRotationRate)) {
                for cell in self.tableView.visibleCells as! [CatPhotoTableViewCell] {
                    cell.panImage(with: yRotationRate)
                }
            }
        })

这段代码的关键在于 self.lastY = deviceMotion.rotationRate.y 这个语句,无论 deviceMotion.rotationRate.y 变化多大,都执行后面的代码,正常应该是 deviceMotion.rotationRate.y 的变化范围超过多少的时候才执行后面的代码,所以优化如下

  motionManager.startDeviceMotionUpdates(to: .main, withHandler:{ deviceMotion, error in
            guard let deviceMotion = deviceMotion else { return }
            guard abs(self.lastY - deviceMotion.rotationRate.y) > 0.1 else { return }
            
            let xRotationRate = CGFloat(deviceMotion.rotationRate.x)
            let yRotationRate = CGFloat(deviceMotion.rotationRate.y)
            let zRotationRate = CGFloat(deviceMotion.rotationRate.z)
            
            print("y \(yRotationRate) and x \(xRotationRate) and z\(zRotationRate)")
            
            if abs(yRotationRate) > (abs(xRotationRate) + abs(zRotationRate)) {
                for cell in self.tableView.visibleCells as! [CatPhotoTableViewCell] {
                    cell.panImage(with: yRotationRate)
                }
            }
        })

修改 self.lastY = deviceMotion.rotationRate.y 的逻辑为 guard abs(self.lastY - deviceMotion.rotationRate.y) > 0.1 else { return }
。当 deviceMotion.rotationRate.y 变化范围超过 0.1 的时候才执行后面的代码逻辑。修改完代码之后进行验证修改效果。

验证修改

使用 Energy Impact 进行验证之后,发现能耗还是非常高,降不下来。那么接下来就继续使用 Instruments 查找原因。

使用 Time Profiler

发现 CatFeedViewController 的 sendLogs() 也是占用了大量的 CPU 时间,接下来使用 Xcode 查看代码。通过代码追溯,找到了 CatFeedViewController 的init() 方法。

 init() {
        super.init(nibName: nil, bundle: nil)
        navigationItem.title = "Catstagram"
        
        tableView.autoresizingMask = UIViewAutoresizing.flexibleWidth;
        tableView.delegate = self
        tableView.dataSource = self
        
        let timer = Timer(timeInterval: 1.0, target: self, selector: #selector(CatFeedViewController.sendLogs), userInfo: nil, repeats: true)
        RunLoop.main.add(timer, forMode: .commonModes)
    }

在这个init() 方法里面发现了一个惊人的代码,有一个定时器每隔 1 s 发起一次 sendlog 的网络请求。不用怀疑了,肯定就是这个坑爹的代码消耗了大量的电量。正常的发送 log 操作应该是在 APP 启动完成的时候发送上次的 log 或者在 APP 进入 applicationWillResignActive 的回调方法发送 log。我们的修改方案是在 APP 进入 applicationWillResignActive 的回调方法发送 log。打开 AppDelegate.swift 文件,添加如下代码同时删除 CatFeedViewController 的init() 方法里面的 sendlog 定时器。

func applicationWillResignActive(_ application: UIApplication) {
        rootVC.sendLogs()
    }

接下来就是验证修改效果了,使用 Energy Impact 这个工具来验证,对于 验证 APP 的能耗概况来说, Energy Impact 工具足以满足需求。

低能耗

现在 APP 的能耗是处于低水平,并且 Network 斌不是一直处于活跃状态。

暂时高能耗

将 APP 退到后台,再进入前台,触发 APP 的 sendlog 操作。这个时候 APP 的能耗进入高等级,但是随后下降到低等级能耗。这个是 APP 的正常表现。

总结

APP 性能优化中,能耗优化决定了用户在同样的电量消耗情况下能使用你的 APP 多长时间。能耗优化的一般步骤如下
1、使用 Energy Impact 查看 APP 能耗概况
2、若是存在高能耗情况,使用 Instruments 的 Energy Log 模板进行细致验证,并配合 Time Profiler 模板抓取代码的运行细节。
3、根据代码的运行细节,判断潜在的问题点,然后修改代码
4、验证修改效果,若是无效,那么重复 2 - 4 步骤

参考

本文是 raywenderlich 的课程笔记,内容参考 Practical Instruments 课程
1、demo下载地址 https://files.betamax.raywenderlich.com/attachments/videos/789/9560e62e-96d3-47e5-b604-5d20c72bf9ee.zip
2、 Energy Log 课程地址
https://videos.raywenderlich.com/courses/74-practical-instruments/lessons/7
3、Energy Impact 官方文档
https://developer.apple.com/library/content/documentation/Performance/Conceptual/EnergyGuide-iOS/MonitorEnergyWithXcode.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容