How does cell reuse work, and what are the classic reuse bugs?
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
prepareForReusecalled? 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… →