How does cell reuse work, and what are the classic reuse bugs?

7.5.3 · L3 · Interview

Predict first

An async image download for row 3 finishes after its cell has been reused for row 40. What is the standard fix?

// A: cancel in prepareForReuse and verify the cell still shows the same item before applying the image
// B: make the download faster
// C: disable reuse
  • A
  • B
  • C

Mental model. A restaurant with a limited number of plates. A plate is washed and reused for the next customer; if it is not cleaned properly, you serve the last customer’s leftovers.

Collection and table views do not create a cell per item. They keep a small pool and reuse cells as they scroll off screen: you ask the view to dequeueReusableCell (or use a CellRegistration / dequeueConfiguredReusableCell), configure it for the new item, and return it. A reused cell still carries whatever state it had before, so configuration must set every piece of state, and prepareForReuse() is the place to reset things that configuration does not touch: cancel pending work, clear images, reset selection or highlight styling. Dequeuing an identifier that was never registered is a hard crash.

The classic bugs are all stale state. Wrong image: an asynchronous image load finishes after the cell was reused for another row and writes the old image into the new one; fix by cancelling in prepareForReuse and checking the cell still represents the same item before applying a result. Duplicated controls or wrong checkmark: state set only in one branch (if isSelected { … } with no else). Accumulating subviews: adding subviews in the configure step instead of the initialiser. Keep init for building the view hierarchy once and configuration for per-item data. Cells should be dumb: do the expensive work (image decoding, formatting) before or off the main thread, not in the cell provider.

The cell is a shared, recycled view. Tie the result to the item it was requested for, and cancel stale work when the cell is reused.

Common traps

  • Applying an async result to a cell without checking it still belongs to that item. The image lands on the wrong row.
  • Setting a flag only in the true branch. The reused cell keeps the old value in the false case.

They might follow up with

  • When is prepareForReuse called? Just before the cell is returned from the dequeue call for a new item.
  • Why build subviews in init? It runs once; configuring runs for every reuse, so creating views there leaks and stacks them.

Related questions

  • 7.5.2 How does a diffable data source work, and what does it require of your item identifiers?
  • 7.5.4 How do prefetching and self-sizing cells keep scrolling smooth, and what can still make it janky?
  • 7.6.1 How does a UITableView get its rows, and why do batch updates crash when the data and the update disagree?

← How does a diffable data source work, and what does it require of…   How do prefetching and self-sizing cells keep scrolling smooth, and… →