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閱讀 230,182評論 6 543
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,489評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,290評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,776評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,510評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,866評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,860評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,036評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,585評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,331評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,536評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,058評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,754評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,154評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,469評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,273評論 3 399
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,505評論 2 379

推薦閱讀更多精彩內容