UITableViewCellの再利用を知る

Satsuki Hashiba
6 min readDec 16, 2019

--

UITableViewは描画されるviewが多く、その実装方法によってはアプリのパフォーマンスを著しく低下させます。本記事では、パフォーマンス対策のために導入されているcellの再利用について解説します。

UITableView

TableViewは、大量にある同じ種類の情報をリスト表示する際に利用します。そのため状況によってはviewの描画数が非常に多くなり、特にスクロール時には、パフォーマンスの低下が懸念されます。これを解決するのがcellの再利用です。毎回新しくviewを作るのではなく以前に生成したcellを利用することで、メモリ割り当てを最小限にします。

Cellの再利用

cellを再利用するために、まず事前にregister(_:forCellReuseIdentifier:)を呼んでおきます。用意したviewをcellのテンプレートとして登録するメソッドです。テンプレートたちはユニークなreuseIdentifierによって管理されます。

次に、cellの生成時、つまりtableView(_:cellForRowAt:)の中でdequeueReusableCell(withIdentifier:for:)を呼びます。このメソッドは、再利用可能なcellがあればそれを、なければ新しく作成したcellを返します。再利用可能なcellは、reuseIdentifierに紐づけられたreuse queueに格納されています。

Reuse Queue

TableViewは裏側でreuse queueというものを持っており、reuseIdentifierごとにreuse queueが存在します。画面外に出たcellは、自身のidentifierに紐づいたreuse queueに追加されます。そして、同じidentifierのcellが表示されようとする時、queueから取り出されます。

Prepare for Reuse

この再利用の仕組みによって、cellの見た目を変更した際に「以前のcellに対する変更が残ってしまう」という問題がしばしば起こります。例えばカスタムセルクラスの中で、データの中に画像が存在しない場合は、cellに設置したimageViewを非表示にする処理を書いたとします。このような実装において、imageViewが非表示となったcellを再利用する時、画像の有無にかかわらずimageViewは非表示のままになってしまいます。

これを解決するのが prepareForReuse()メソッドです。これはUITableViewCellのインスタンスメソッドで、cellが再利用される前に呼ばれます。この中にcellの見た目をデフォルトの状態に戻す処理を書きます。上の例でいうと、imageView.isHiddenfalseにするなどの処理です。こうすることで、再利用時に前の変更が残ってしまうことを防ぎます。また、デフォルト実装を切り分けることができるので、平易なコードを維持します。

dequeue系メソッド

再利用可能なcellを取得するメソッドは、dequeueReusableCell(withIdentifier:for:)の他にdequeueReusableCell(withIdentifier:)というものが存在します。こちらはiOS 5以前から使われていたものです。

まず挙げられる違いとして、古いメソッドは引数にindexPathを渡さないという点です。新しいメソッド(=dequeueReusableCell(withIdentifier:for:))はindexPathを渡していますが、これはtableView(_:heightForRowAt:)になどで行われる追加の設定をチェックするために用いられます。

次に挙げられる違いは、古いメソッドは返り値がoptionalである点です。先ほどcellの生成前にはregisterメソッドを呼ぶ必要があると述べましたが、dequeueReusableCellメソッド渡されたidentifierと一致するテンプレートが登録されていなかった場合、新しいメソッドはクラッシュしますが、古いメソッドはnilを返します。

公式ドキュメントの中では、古いメソッドはcellの取得のみの挙動に対し、新しいメソッドはcellの取得およびtableViewへの追加を行うような記述が見られます。この違いは定かではありませんが、新しいメソッドはtableView(_:cellForRowAt:)外では呼び出さないようにと明言されています。したがって、tableView(_:cellForRowAt:)内ではindexPathが必要となる新しいメソッドを、tableView(_:cellForRowAt:)外でcellを生成しなければならない場合にはindexPathを必要としない古いメソッドを呼ぶと考えるのが良いです。

// Inside of tableView(_:cellForRowAt:) dequeueReusableCell(withIdentifier:for:)// Outside of tableView(_:cellForRowAt:)
dequeueReusableCell(withIdentifier:)

以上でUITableViewCellの再利用についての解説を終わります。TableViewはアプリのパフォーマンスを大きく左右する箇所なので、きちんと理解した上で実装していきたいですね。

参考文献

--

--