読者です 読者をやめる 読者になる 読者になる

なるようになるかも

力は多くの場合、その人の思いを超えない。

estimatedHeightについて計測してみた。

iOS - 見積もりの高さでUITableViewを高速化する話。 - Qiita

iOS6検証用だったiPhone4Sを、速度テスト用に初期化してiOS7に上げたのはいいんだけど、Apple Developerのダウンタイムだったみたいで、結局テスト機はiPhone5Sを使うことに。

そのためテストデータ件数が実際にはありえない数字になってます。本当は実際的な数字で確かめてみたかったんだけどね…。iPhone5Sだと処理が一瞬で終わってテストにならないんです…。

rowHeightプロパティの効果

まずUITableViewrowHeightプロパティと、tableView:heightForRowAtIndexPath:デリゲートメソッドで高さを計算した場合の比較。

[self.tableView reloadData]で高さを再計算する時間を計測しています。

テストデータ数 1,000,000 件 (試行10回程度の平均)

取得方法 処理時間(sec)
プロパティ 0.032
デリゲート 1.230

rowHeightrowの数だけ掛け算しているだけのプロパティ設定の方が圧倒的に早いという結果が得られましたが、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→スクロール操作、を行っています。

f:id:quesera2:20140216165725p:plain

未設定時はこんな感じです。reloadDataを呼び出すと完全に処理がストップしているのが分かります。

estimatedHeight設定時

f:id:quesera2:20140216170652p:plain

設定時はこうなります。

スクロール時に動的に高さの計算を行っているため、通常より処理が重くなっているのが分かりますが、iPhone5Sではもともとの描画処理の負荷と比較してたいした計算量ではないらしく、スクロール処理に引っかかりが生じるということもありませんでした。

ここでは10万件という極端なケースを挙げましたが、データ件数が多い場合には、reloadDataで全てのindexPathに対して実際の高さを取得しないというメリットは大きいと思います。

一方でデータ件数は多くないものの、レイアウトが複雑であったり(ここでは単一のUILabelを持つセルとしましたが、複数のラベルがある場合その分計算量が倍増します)、データの読み込みにIOアクセスが伴う場合、高さの値を事前計算した方がスクロール操作が快適になる可能性があります。