説得力が無いせいか、最近説明するのがしんどいのでメモ書き。
アプリがガタガタする
なんてことあってはならんのです。
例えば画面をびょーんとスクロールした時なんかにガタガタってぎこちない動きするアレです。
いちいち説明するまでもなく見れば分かる事なのですが、どうも人によっては問題とは考えてくれないようなので今更ながらにあえて前置きだけの話をします。
何故にガタガタするかといえばフレームレートに処理が追いついてないのです。
おさらい
すごく端折ります。
フレームレートに追いついてない(フレーム落ち・処理落ち)なんてのは、古来ファミコンの時からある話で、ときどきぎこちない動きでガタガタってするアレです。昔のゲーム開発者はインターレスとて29.997fpsで垂直帰線期間しか画面I/O叩けないとかなんとかで泣くほど苦労し、昔のゲーマーはいらいらしつつもたまにグラ3やR-TYPE2周目7面後半復活で助かったりするアレです。フレーム落ち話だけで酒が飲めそうです。
そんな時から20〜30年ほど経ったiPhoneとて今だにこの話は残っていて、秒間60フレーム(60fps)で画面を定期的に書き換えて表示しています。
時間にすると 1/60 = 0.016666...秒 = 約16ms、この時間内で処理を終えタイミングよく回っていれば問題ないのですが、処理時間などがかかってしまいこの時間をオーバーすると次の書き換えタイミングに回されてしまい、結果正しいタイミングで書き換えがされずフレームが落ち、ガタついてしまうわけです。
iOSは原則として表示される画面の処理についてはメインスレッドのみで行わなければなりません。
iOSはイベント駆動で処理されますから、OSから何らかのコールバックが来ます。
特に画面系のコールバックはメインスレッドでやって来ます。
つまりは、このメインスレッドにおけるコールバックを16ms以内に返さないと画面更新のタイミングに間に合わず先送りされてしまい、ガタガタしてしまうってわけです。
確認方法
そんなの見りゃわかる、ちょっとスクロールとかさせればわかる、蛍光灯の前で手でも振ればわかる、格ゲーなんかの人なら1フレーム単位で見えてる。
なんですが、実際のところよほど慣れているかやばい薬でもキメて脳内クロック上がってるとかでないと難しいですし、どこで何フレームずれたかなんてのはさすがに見た目で数えられるものじゃありません。
なので数値として出した方がよいです。
計測といえばXcodeでinstrumentsなのですが、これは描画された時の数値しか出なくていまいち希望の値が取れません。
別の方法として、iOSでは画面更新毎の時間をCADisplayLinkで取得することができます。
CADisplayLinkでは画面更新毎にコールバックを受け取る事が出来、これの.timestampが前回の画面更新時間になります。
ちなみに、.durationはしっかり 1/60 = 0.0166666...ですね。
よって、このコールバック毎に前回とのtimestampの差を取ってやれば画面更新間の時間が拾え、フレーム落ちしているかどうか判断できます。
計測ライブラリ
計測方法はわかったけれど書くのも面倒なのでライブラリに頼ります。
RRFPSBarというライブラリがうまく出来ているのですが、もう少し見た目、ライブラリ自体の負荷、他のデータなどをよくしたかったのでちょっと書いてみました。
FPSStatusBar https://github.com/asaday/FPSStatusBar
cocoapodsやcarthageで入れて、importとstart()の2行ほど付け足せばステータスバーのところに、fps及びcpu使用量などの値を表示できます。
どこで起きるか
ゲーム系はまた別として、UIKitでフレーム落ちはどこで起きるか?
ライブラリを入れて視覚化すると結構色々なところでフレーム落ちはあります。
ですが正直に全部をクリアする必要はありません、目につく部分だけやればOKです。
どこが目につくかといえば、やはりテーブルなどのスクロール系、アニメーション系、ユーザーによるドローなど画面が連続的に動く箇所になります。
逆にアクションの始めや画面遷移の始めなど、静から動の瞬間は結構ごまかせます。
どう改善するか
どこの画面のどんな操作でフレーム落ちが発生するか分かれば、そこにメインスレッドにおける処理が16ms以上かかっている箇所があるハズです。
やるべきことはメインスレッドで必要でない処理はバックグラウンド処理に回すことです。
メインスレッドで処理せざるを得ないものは画面系ですので、逆に画面系でない処理は極力バックグラウンドにて処理します。
これが自分では十分そんなこと対応していると思いきや色々なものが起因してメインスレッドで動いていたりするんですよね。
あれやこれやと色々な方法はあるのですが、まぁここまでの話が通じていればようやく前置き終わりです。はぁ。