はじめに
こんにちは
アプリ内で利用する機会はそれほどでもないのですが、定期実行するTimerについて調べてみたのでメモとして書いてみたいと思います。(Xcode: 9.1, Swift:4.0.2)
至らぬ点などコメント頂けたら幸いです。
Timerの基本的な利用
Timerはインスタンス生成即座に起動されるようです。
ここで注意点なのですが「fire() = 起動」を意味するものではなく、fire()は、Timerインスタンスが破棄されていない状態にて、すぐにスケジューリングしていたメソッドを呼びたい時に使うためのメソッドのようです。
import UIKit
class MainViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(MainViewController.timerUpdate), userInfo: nil, repeats: true)
}
@objc func timerUpdate() {
print("update")
}
}
//5秒間隔で呼ばれる
//5秒後...
//update
//5秒後...
//update
名前 | 説明 |
---|---|
timeInterval | ループなら間隔,1度きりなら発動までの秒数 |
target | メソッドを持つオブジェクト |
selector | 実行するメソッド |
userInfo | オブジェクトに付ける値 |
repeats | 繰り返し実行するかどうか |
Timerをtargetの変数(ここではMainViewController)に保持した場合などの色々なパターンを書いてみたいと思います。
scheduledTimerを利用するパターン
scheduledTimerでTimerを生成した場合は、内部的に現在の実行ループ(通常はメインスレッド)にてスケジュール登録されるようです。
オブジェクトを保持しない
import UIKit
class MainViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
Timer.scheduledTimer(timeInterval: 5, //ループなら間隔 1度きりなら発動までの秒数
target: self, //メソッドを持つオブジェクト
selector: #selector(MainViewController.timerUpdate), //実行するメソッド
userInfo: nil, //オブジェクトに付けて送信する値
repeats: true) //繰り返し実行するかどうか
}
@objc func timerUpdate() {
print("update")
}
}
//5秒間隔で呼ばれる
//5秒後...
//update
//5秒後...
//update
オブジェクトを保持する
import UIKit
class MainViewController: UIViewController {
var timer: Timer?
override func viewDidLoad() {
super.viewDidLoad()
self.timer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(MainViewController.timerUpdate), userInfo: nil, repeats: true)
}
@objc func timerUpdate() {
print("update")
}
@IBAction func buttonAction(_ sender: Any) {
print("invalidate!!")
self.timer?.invalidate()
}
}
//5秒間隔で呼ばれる
//5秒後...
//update
//5秒後...
//update
//invalidate!!
//.......
変数を保持しておくことでinvalidate()でタイマーを破棄することができ、実質タイマーが停止します。
Timerインスタンスは破棄されるので、同じインスタンスでの再開は不可能です。再開する場合は破棄した時点の時間を計算するなど、Timerインスタンスを生成し直す必要があります。
変数に対して、重ねてscheduledTimerをした場合
import UIKit
class MainViewController: UIViewController {
var timer: Timer?
override func viewDidLoad() {
super.viewDidLoad()
self.timer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(MainViewController.timerUpdate), userInfo: nil, repeats: true)
print(self.timer)
self.timer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(MainViewController.timerUpdate2), userInfo: nil, repeats: true)
print(self.timer)
}
@objc func timerUpdate() {
print("update")
}
@objc func timerUpdate2() {
print("update2")
}
@IBAction func buttonAction(_ sender: Any) {
print("invalidate!!")
self.timer?.invalidate()
}
}
//Optional(<__NSCFTimer: 0x1c017c500>)
//Optional(<__NSCFTimer: 0x1c017e600>)
//5秒後...
//update
//update2
//5秒後...
//update
//update2
//invalidate!!
//5秒後...
//update
//5秒後...
//update
//invalidate!!
//5秒後...
//update
内部的に実行ループに登録されるようで、2回目の上書きでも、invalidate
後に1回目で生成したTimerが呼び出されてしまいます。
Timer.initを利用するパターン
イニシャライザでTimerを生成した場合は、自ら実行ループ(通常はメインスレッド)にスケジュール登録をする必要があります。
import UIKit
class MainViewController: UIViewController {
var timer: Timer?
override func viewDidLoad() {
super.viewDidLoad()
self.timer = Timer(timeInterval: 5, target: self, selector: #selector(MainViewController.timerUpdate), userInfo: nil, repeats: true)
//登録
RunLoop.main.add(self.timer!, forMode: .defaultRunLoopMode)
}
@objc func timerUpdate() {
print("update")
}
@IBAction func buttonAction(_ sender: Any) {
print("invalidate!!")
self.timer?.invalidate()
}
}
//5秒間隔で呼ばれる
//5秒後...
//update
//5秒後...
//update
//invalidate!!
//.......
変数に対して、重ねてinitをした場合
import UIKit
class MainViewController: UIViewController {
var timer: Timer?
override func viewDidLoad() {
super.viewDidLoad()
self.timer = Timer(timeInterval: 5, target: self, selector: #selector(MainViewController.timerUpdate), userInfo: nil, repeats: true)
self.timer = Timer(timeInterval: 5, target: self, selector: #selector(MainViewController.timerUpdate2), userInfo: nil, repeats: true)
RunLoop.main.add(self.timer!, forMode: .defaultRunLoopMode)
}
@objc func timerUpdate() {
print("update")
}
@objc func timerUpdate2() {
print("update2")
}
@IBAction func buttonAction(_ sender: Any) {
print("invalidate!!")
self.timer?.invalidate()
}
}
//5秒間隔で呼ばれる
//5秒後...
//update2
//5秒後...
//update2
//invalidate!!
//.......
こちらは上書きされた後に自らケジュール登録をしたので、2回目のTimerのみが呼ばれます。
タイマーが呼ばれているタイミング
iPhone
ホームボタンを押して、アプリがバックグランドになると、タイマーのメソッド呼び出しが止まり、再びアクティブになると呼び出しが戻るようです。
AppleWatch
digital crownを押してHomeに戻っても、Timerは呼ばれ続けるようです。任意で止めたい場合はwillActivate()やdidDeactivate()でコントロールする必要があるみたいです。
参考にさせていただいた記事
見て頂いてありがとうございます。