iOS 定時器耗電探究
iOS開發中的幾種定時器
iOS開發中定時器實現方式大致有三種,一種是Timer
實現,一種是通過GCD
自己建立,另一種是CADisplayLink
建立。
Timer
使用簡單,需要注意的是Timer
和runloop
聯絡緊密,在用scheduled方式建立之後,程式會自動將其新增到runloop中,不再需要Timer時,必須手動將其釋放。
CADisplayLink
與螢幕重新整理率同步,也就是每當螢幕重新整理一次就可以執行一次繫結的事件(iPhone的重新整理頻率為60HZ,正常情況下1s需要重新整理60次),也可以通過其屬性設定間隔多少幀執行一次,一般用於即時畫面渲染,啟動CADisplayLink
定時器需要呼叫add(to: <#T##RunLoop#>, forMode: <#T##RunLoopMode#>)
方法。
gcd定時器主要用於精準定時,它的精度能達到納秒級別。gcd不同於上面兩種定時器,它不會受runloop的影響(runloop也是通過gcd實現),所以精確,例如Timer加入runloop預設模式時,當有滑動控制元件在滑動時會暫停計時,但是gcd不會。gcd可控性強,但是使用稍微複雜
定時器普通模式下不能在後臺執行,需要設定Background Modes,選擇一種可以後臺執行的模式
關於耗電測試
有時候開發需要多個任務定時執行(與硬體結合開發較常見),可能他們的定時時間不一致,這就涉及到多個計時。
我試想了兩種實現方式,一種是通過建立多個定時器實現,另一種是建立一個定時器,每個定時任務定義一個計數器,定時器每秒累加他們的計數器,當滿足時間條件時就去執行相應的定時任務,當需要取消某個任務時就停止累加並將計數器置零。
比較一下這兩種方式,第一種簡單明瞭,便於維護,但是會存在多個定時器,消耗記憶體資源。第二種只有一個定時器,但是程式碼易讀性相對第一種較差,也會建立很多全域性變數判斷是否需要累加計數器以及儲存計數器的值。而且定時器執行間隔是1s,執行內容是將所有任務計數器累加1,判斷每個任務的計數器值是否滿足執行條件,耗費CPU資源。
雖然每個定時器都會消耗資源,但我個人還是認為第一種方式較為科學,耗電也不會比第二種高,因為第二種每秒都會執行任務消耗CPU,比如一個10秒的定時任務,第一種方式執行1次,第二種方式需要執行10次,也不夠靈活,管理多個任務的時候更難操控。接下來我需要驗證一下這個想法,我準備先用Timer實現驗證。最後附帶gcd實現定時器的方式。
本次驗證耗電採用Instrument
的Energy Log
工具,測試流程如下:
1.設定app不鎖屏:
UIApplication.shared.isIdleTimerDisabled = true
2.安裝APP後斷開iPhone電源線
3.退出所有執行的APP
4.開啟
系統設定-開發者-logging
開啟Energy
後點選Start Recording
5.開啟需要測試的APP執行10分鐘
6.開啟
系統設定-開發者-logging
點選Stop Recording
7.開啟
Instrument
的Energy Log
工具,從裝置匯出日誌
8.修改方式後重新執行以上步驟
驗證第一種實現方式
定義6個定時器控制6個任務,每個任務時間不一致。
為了有操作消耗CPU資源,這裡任務都是計算0-10000之間的完全數
完全數(Perfect number),又稱完美數或完備數,是一些特殊的自然數。它所有的真因子(即除了自身以外的約數)的和(即因子函式),恰好等於它本身。如果一個數恰好等於它的因子之和,則稱該數為“完全數”。
import UIKit
class ViewController: UIViewController {
fileprivate var timer1: Timer?
fileprivate var timer2: Timer?
fileprivate var timer3: Timer?
fileprivate var timer4: Timer?
fileprivate var timer5: Timer?
fileprivate var timer6: Timer?
override func viewDidLoad() {
super.viewDidLoad()
UIApplication.shared.isIdleTimerDisabled = true
lotsTimer()
}
func lotsTimer(){
timer1 = Timer.scheduledTimer(timeInterval: 20, target: self, selector: #selector(timer1Event), userInfo: nil, repeats: true)
timer2 = Timer.scheduledTimer(timeInterval: 30, target: self, selector: #selector(timer2Event), userInfo: nil, repeats: true)
timer3 = Timer.scheduledTimer(timeInterval: 40, target: self, selector: #selector(timer3Event), userInfo: nil, repeats: true)
timer4 = Timer.scheduledTimer(timeInterval: 50, target: self, selector: #selector(timer4Event), userInfo: nil, repeats: true)
timer5 = Timer.scheduledTimer(timeInterval: 60, target: self, selector: #selector(timer5Event), userInfo: nil, repeats: true)
timer6 = Timer.scheduledTimer(timeInterval: 70, target: self, selector: #selector(timer6Event), userInfo: nil, repeats: true)
}
@objc func timer1Event(){
print("timer1執行")
envent()
}
@objc func timer2Event(){
print("timer2執行")
envent()
}
@objc func timer3Event(){
print("timer3執行")
envent()
}
@objc func timer4Event(){
print("timer4執行")
envent()
}
@objc func timer5Event(){
print("timer5執行")
envent()
}
@objc func timer6Event(){
print("timer6執行")
envent()
}
func envent(){
for i in 2...10000{
var sum = 1
var j = 2
while j <= Int(sqrt(Double(i))){
if i % j == 0{
sum += j
if i / j != j{
sum += i / j
}
}
j += 1
}
if sum == i {
print(i)
}
}
}
}
按照上面的測試步驟,斷開電源線執行10分鐘。
驗證第二種方式
由於第一種方式所有定時器都是重複計時操作,為保證一致,這裡只定義6個全域性變數儲存每個事件的計數器值,少定義6個全域性變數判斷是否停止累加計數器(實際開發中用這種方式很繁雜,也許會停掉事件,但計時器掌管其他事件,不能停止,所以只能通過變數判斷是否需要繼續計數)。
import UIKit
class ViewController: UIViewController {
fileprivate var timer: Timer?
fileprivate var counter1 = 0
fileprivate var counter2 = 0
fileprivate var counter3 = 0
fileprivate var counter4 = 0
fileprivate var counter5 = 0
fileprivate var counter6 = 0
override func viewDidLoad() {
super.viewDidLoad()
UIApplication.shared.isIdleTimerDisabled = true
oneTimer()
}
func oneTimer(){
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(addCount), userInfo: nil, repeats: true)
}
@objc func addCount(){
addCount1()
addCount2()
addCount3()
addCount4()
addCount5()
addCount6()
}
func addCount1(){
counter1 += 1
guard counter1 >= 20 else { return }
timer1Event()
counter1 = 0
}
func addCount2(){
counter2 += 1
guard counter2 >= 30 else { return }
timer2Event()
counter2 = 0
}
func addCount3(){
counter3 += 1
guard counter3 >= 40 else { return }
timer3Event()
counter3 = 0
}
func addCount4(){
counter4 += 1
guard counter4 >= 50 else { return }
timer4Event()
counter4 = 0
}
func addCount5(){
counter5 += 1
guard counter5 >= 60 else { return }
timer5Event()
counter5 = 0
}
func addCount6(){
counter6 += 1
guard counter6 >= 70 else { return }
timer6Event()
counter6 = 0
}
func timer1Event(){
print("timer1執行")
envent()
}
func timer2Event(){
print("timer2執行")
envent()
}
func timer3Event(){
print("timer3執行")
envent()
}
func timer4Event(){
print("timer4執行")
envent()
}
func timer5Event(){
print("timer5執行")
envent()
}
func timer6Event(){
print("timer6執行")
envent()
}
func envent(){
for i in 2...10000{
var sum = 1
var j = 2
while j <= Int(sqrt(Double(i))){
if i % j == 0{
sum += j
if i / j != j{
sum += i / j
}
}
j += 1
}
if sum == i {
print(i)
}
}
}
}
測試結果
電量
CPU
測試結果可以看出,耗電量兩個不相上下(20個等級,數字越大越耗電,這裡全部是0),因為操作都過於簡單,幾乎不耗電,但是從CPU佔用情況看。方式1更節約CPU資源,而CPU資源消耗跟耗電量成正比,所以有理由相信當APP執行時間足夠長,方式2會消耗更多電量。
GCD方式實現定時器
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
createDispatchTimer(timeInterval: 2, handler: { [weak self] (timer) in
self?.envent()
//需要取消重複時呼叫
//timer?.cancel()
}, needRepeat: true)
}
/// - timeInterval: 間隔時間
/// - handler: 事件
/// - needRepeat: 是否重複
func createDispatchTimer(timeInterval: Double, handler:@escaping (DispatchSourceTimer?)->(), needRepeat: Bool)
{
let timer = DispatchSource.makeTimerSource(flags: [], queue: DispatchQueue.main)
timer.schedule(deadline: .now(), repeating: timeInterval)
timer.setEventHandler {
DispatchQueue.main.async {
if needRepeat{
handler(timer)
}else{
timer.cancel()
handler(nil)
}
}
}
timer.resume()
}
func envent(){
print("執行")
}
//附贈
/// GCD定時器倒數計時
/// - timeInterval: 間隔時間
/// - repeatCount: 重複次數
/// - handler: 迴圈事件, 閉包引數: 1. timer, 2. 剩餘執行次數
func createDispatchTimer(timeInterval: Double, repeatCount:Int, handler:@escaping (DispatchSourceTimer?, Int)->())
{
if repeatCount <= 0 {
return
}
let timer = DispatchSource.makeTimerSource(flags: [], queue: DispatchQueue.main)
var count = repeatCount
timer.schedule(wallDeadline: .now(), repeating: timeInterval)
timer.setEventHandler(handler: {
count -= 1
DispatchQueue.main.async {
handler(timer, count)
}
if count == 0 {
timer.cancel()
}
})
timer.resume()
}
}
相關文章
- iOS定時器iOS定時器
- iOS12省電攻略大全,iOS12耗電情況 iOS12耗電嗎?iOS
- iOS11.4.1耗電快嗎?iOS11.4.1耗電情況怎麼樣?iOS
- 探究 | App Startup真的能減少啟動耗時嗎APP
- iOS中的3種定時器iOS定時器
- iOS主執行緒耗時檢測方案iOS執行緒
- iOS Block探究iOSBloC
- iOS倒數計時的探究與選擇iOS
- iOS 深入探究 AutoreleasePooliOS
- iOS - Block探究系列一iOSBloC
- 高德APP啟動耗時剖析與優化實踐(iOS篇)APP優化iOS
- iOS底層原理探究-RunloopiOSOOP
- iOS底層原理探究-RuntimeiOS
- iOS之Wifi開發探究iOSWiFi
- 定時器以及定時器的幾個案例定時器
- 震驚!iOS11 耗電竟然是因為它...——Jinkey 原創iOS
- 4.看門狗、定時器、觸控電容定時器
- JavaScript定時器JavaScript定時器
- Timer(定時器)定時器
- JavaScript 定時器JavaScript定時器
- js定時器JS定時器
- python定時器Python定時器
- iOS-效能優化深入探究iOS優化
- iOS - 對 block 實現的探究iOSBloC
- 探究 iOS 記憶體問題iOS記憶體
- 利用定時器實現51微控制器返回上電執行時間功能定時器
- Android 快速定位耗時方法Android
- systrace 統計方法耗時
- 純生JS輪播,定時器新增和清除定時器JS定時器
- Linux核心定時器Linux定時器
- JMeter—定時器(八)JMeter定時器
- Flink onTimer定時器定時器
- Qt - QTimer(定時器)QT定時器
- python flask 定時器PythonFlask定時器
- TIM-定時器定時器
- Java - Timer定時器Java定時器
- Systemd 定時器教程定時器
- java 中定時器Java定時器