estimatedHeightについて計測してみた。
iOS - 見積もりの高さでUITableViewを高速化する話。 - Qiita
iOS6検証用だったiPhone4Sを、速度テスト用に初期化してiOS7に上げたのはいいんだけど、Apple Developerのダウンタイムだったみたいで、結局テスト機はiPhone5Sを使うことに。
そのためテストデータ件数が実際にはありえない数字になってます。本当は実際的な数字で確かめてみたかったんだけどね…。iPhone5Sだと処理が一瞬で終わってテストにならないんです…。
rowHeightプロパティの効果
まずUITableView
のrowHeight
プロパティと、tableView:heightForRowAtIndexPath:
デリゲートメソッドで高さを計算した場合の比較。
[self.tableView reloadData]
で高さを再計算する時間を計測しています。
テストデータ数 1,000,000 件 (試行10回程度の平均)
取得方法 | 処理時間(sec) |
---|---|
プロパティ | 0.032 |
デリゲート | 1.230 |
rowHeight
をrow
の数だけ掛け算しているだけのプロパティ設定の方が圧倒的に早いという結果が得られましたが、iPhone5Sでは効果が現れるのは50万件を超える辺り。iPhone4での閾値はどの辺りになるんでしょう。
「高さ固定であればtableView:tableView heightForRowAtIndexPath:
は実装しない」というのが間違いはなさそうです。
estimatedHeightプロパティの効果
10~256文字のランダムな文字列長データを生成しメモリに保持し、boundingRectWithSize:options:attributes:context:
で高さを計測しています。このとき、estimatedHeight
を設定した場合とそうでない場合を比較します。
テストデータ数 100,000 件 (試行10回程度の平均)
estimatedHeight | 処理時間(sec) |
---|---|
未設定 | 6.841 |
設定 | 0.013 |
計算を先送りにするので当然reloadData
は早くなるのですが、それにしても圧倒的です。
先送りされた場合、Cellが表示されるタイミングでtableView:heightForRowAtIndexPath:
が評価されますが、計算結果は保持されるようで、同じindexPath
に対して2回以上呼び出されることはありません。
高さ計算、contentSize
再設定がスクロール時に行われるため、その負荷をInstrumentsで確かめてみました。
estimatedHeight未設定時
アプリ起動→スクロール操作→reloadData
→スクロール操作、を行っています。
未設定時はこんな感じです。reloadData
を呼び出すと完全に処理がストップしているのが分かります。
estimatedHeight設定時
設定時はこうなります。
スクロール時に動的に高さの計算を行っているため、通常より処理が重くなっているのが分かりますが、iPhone5Sではもともとの描画処理の負荷と比較してたいした計算量ではないらしく、スクロール処理に引っかかりが生じるということもありませんでした。
ここでは10万件という極端なケースを挙げましたが、データ件数が多い場合には、reloadData
で全てのindexPath
に対して実際の高さを取得しないというメリットは大きいと思います。
一方でデータ件数は多くないものの、レイアウトが複雑であったり(ここでは単一のUILabel
を持つセルとしましたが、複数のラベルがある場合その分計算量が倍増します)、データの読み込みにIOアクセスが伴う場合、高さの値を事前計算した方がスクロール操作が快適になる可能性があります。