Instruments 之 Energy Log

本文介绍如何使用Xcode的EnergyImpact工具和Instruments的EnergyLog模板,配合TimeProfiler模板,定位并解决iOS APP高能耗问题,通过修改代码逻辑降低能耗。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

对于生活离不开手机的我们来说,手机的电量就是一条重要的生命线,一般来说,当电量低于 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



作者:要上班的斌哥
链接:https://www.jianshu.com/p/d7091ec727a7
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Xcode Instruments 是 Apple 提供的一款强大的性能分析工具,主要用于帮助开发者优化 iOS、macOS 应用的性能。它可以检测内存泄漏、CPU 使用率、能源消耗、图形渲染等多个方面的问题。 ### 启动 InstrumentsXcode 中启动 Instruments 有多种方式: 1. **通过 Xcode 的菜单栏**: - 在 Xcode 中打开你的项目。 - 点击顶部菜单栏的 `Product` > `Profile`,这将直接启动 Instruments 并附着到当前运行的应用上[^1]。 2. **使用 Profile in Instruments 按钮**: - 在 Xcode 的调试指示器中(如 CPU、内存等),点击 `Profile in Instruments` 按钮可以快速启动 Instruments 并开始分析对应的性能指标。 - 注意,并不是所有的 Xcode 指示器都有这个按钮,例如 Energy Impact 指示器需要手动选择不同的模板来启动分析会话[^1]。 3. **从 Instruments 应用程序启动**: - 打开 Finder,进入 `Applications` > `Xcode.app` > `Contents` > `Developer` > `Instruments.app`。 - 直接双击 `Instruments.app` 启动应用程序,然后选择要分析的目标设备和应用。 ### 选择分析模板 Instruments 提供了多种预定义的模板,适用于不同的性能分析需求: - **Time Profiler**:用于分析 CPU 使用情况,找出占用 CPU 时间最多的函数调用。 - **Allocations**:用于跟踪内存分配,帮助发现内存泄漏。 - **Leaks**:专门用于检测内存泄漏。 - **Energy Log**:用于分析应用的能源消耗。 - **Core Animation**:用于分析图形渲染性能,特别是与 Core Animation 相关的部分。 - **Network**:用于监控网络请求和数据传输。 你可以在启动 Instruments 时选择一个合适的模板,或者在 Instruments 运行时动态添加或删除工具。 ### 配置自动使用的分析模板 如果你经常需要使用相同的模板去分析你的应用,可以设置 Xcode 项目以在启动分析时自动使用特定的 Instruments 分析模板: 1. 打开 Xcode 项目。 2. 点击顶部菜单栏的 `Product` > `Scheme` > `Edit Scheme`。 3. 在左侧选择 `Profile` 配置。 4. 在右侧的 `Instrument` 下拉菜单中选择你希望使用的模板。 5. 点击 `Close` 保存设置。 这样,每次点击 `Product` > `Profile` 时,Xcode 将自动使用你指定的模板启动 Instruments[^2]。 ### 基本操作指南 1. **启动分析会话**: - 选择好模板后,点击左上角的 `Record` 按钮(圆形图标)开始录制数据。 - 应用将会启动并开始收集性能数据。 2. **停止分析**: - 点击 `Stop` 按钮(方形图标)结束录制。 - 此时你可以查看收集到的数据,并进行进一步的分析。 3. **导出数据**: - 如果需要保存分析结果,可以选择 `File` > `Export Trace` 将数据导出为 `.trace` 文件,方便后续查阅或分享。 4. **深入分析**: - 根据所选的模板,Instruments 会提供相应的视图和工具来帮助你深入分析问题。 - 例如,在 `Time Profiler` 中,你可以看到各个函数调用的时间分布;在 `Leaks` 中,你可以查看内存泄漏的具体位置。 ### 示例代码:使用 Time Profiler 分析 CPU 使用情况 ```swift import Foundation func heavyComputation() { let startTime = CFAbsoluteTimeGetCurrent() // 模拟耗时计算 for i in 0..<1000000 { let _ = sqrt(Double(i)) } let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime print("Time elapsed: $timeElapsed) seconds") } heavyComputation() ``` 在这个简单的 Swift 示例中,我们模拟了一个耗时的计算任务。通过使用 Xcode Instruments 的 `Time Profiler` 工具,可以清晰地看到 `heavyComputation()` 函数的执行时间分布,从而判断是否存在性能瓶颈。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值