この記事はQuadアドベントカレンダー7日目の記事です
前回wift用アニメーションエンジンを作り始めるからの第2回目です。
今回はアニメーション用のスレッドを別に用意して、CADisplayLinkを利用する方法を模索したいと思います。メインスレッドは何かと処理が重なると思うので、サブスレッドに分けてそちらでアニメーション専用で利用すればパフォーマンスも良くなるのではと考えています。
色々調べたところ、どうやらRunLoopを作る必要があるところまではわかりました。
RunLoopを作るには
実行ループを設定するために必要な作業は、スレッドを起動し、実行ループオブジェクトの参照を取得し、イベントハンドラを組み込んで、実行ループに実行を指示することだけです。長時間存続する二次スレッドを作成する場合は、そのスレッド用の実行ループを独自に設定する必要があります。
RunLoopはCADisplayLinkの設定でも使用したと思いますが、サブスレッドを切らない限りはRunLoop.currentでメインスレッドの実行ループを取得できます。
何度実行しても一つのスレッド内では同一のRunLoopを取得します。シングルトンみたいですね。
別スレッド内でRunLoop.currentすると新しいじ実行ループを取得できます。RunLoop.currentのタイミングで新規さ作成されるようです。
実際に作ってみる
RunLoopはThreadに紐づくようです。そのためまずはThreadを作る必要があります。
作るのは簡単です。
override func viewDidLoad() {
super.viewDidLoad()
let thread = Thread(
target: self,
selector: #selector(self.threadLoop),
object: nil
)
thread.start()
}
@objc func threadLoop() -> Void {
print("thread loop done!")
}
起動するとコンソールに「thread loop done!」が表示されると思います。この処理では、単に別スレッドで処理を実行したというだけのようです。
RunLoopを取得する
threadLoop()内で、RunLoop.currentを実行すると、サブスレッドのRunLoopが取得できます。RunLoopは一つのスレッドに対して、一つだけ存在します。
@objc func threadLoop() -> Void {
print("thread loop done!")
let runLoop = RunLoop.current
}
CADisplayLinkを利用する
runLoopが取得できたのでCADisplayLinkを利用してアニメーションをエンジンを組み立てます。
@objc func threadLoop() -> Void {
print("threadLoop done")
self.updater = CADisplayLink(target: self, selector: #selector(self.onLoop(_:)))
updater!.preferredFramesPerSecond = 60
updater?.add(to: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode)
}
@objc func onLoop(_ sender:AnyObject ) -> Void {
print("CADisplay loop done!!")
}
しかしこれでは、onLoop()のprintが実行される気配がありません。上記実装では、threadLoop()が実行されたタイミングで、Threadは終了し、RunLoopも消滅するためです。
スレッド用の実行ループを独自に設定
スレッド〜キープするために、ループ処理を利用します。
@objc func threadLoop() -> Void {
self.updater = CADisplayLink(target: self, selector: #selector(self.onLoop(_:)))
updater!.preferredFramesPerSecond = 60
updater?.add(to: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode)
// スレッドをキープする
while self.isRunning {
// ここの数値はFPS設定に影響しています。1/60 = 60FPS
RunLoop.current.run(until: Date(timeIntervalSinceNow: 1/60))
}
}
これでサブスレッドはキープされています。コンソール上に「CADisplay loop done!!」が大量に表示されると思います。
Swiftでスレッド周りの情報はGCD周りぐらいしかないので、おそらく普通は使わないと思いますが、触ってみると面白いですね。
続きは次回へ