Earlier in this chapter, you learned about cell-reuse in table views. You assigned a reuse-identifier to a table-view cell so that the table view would know which cell it should use to display contacts in. Cell-reuse is a concept that is applied so a table view can reuse cells that it has already created. This means that the only cells that are in memory are either on the screen or barely off the screen. The alternative would be to keep all cells in memory, which could potentially mean that hundreds or thousands of cells are held in memory at any given time. For a visualization of what cell reuse looks like, have a look at the following diagram:
As you can see, there are just a few cells in the picture that are not on the visible screen. This roughly equals the number of cells that a table view might keep in memory. This means that regardless of the total amount of rows you want to show, the table view has a roughly constant pressure on your app's memory usage.
Earlier, you witnessed a bug that showed the wrong image next to a contact in the table view. This bug is related to cell-reuse because the wrong image is actually only visible for contacts that don't have their own image. This means that the image from the contact that was previously shown in that particular cell is now shown for a different contact.
A cell is first created when dequeueReusableCell(withIdentifier:) is called on the table view and it does not have an unused cell available. Once the cell is either reused or created, prepareForReuse() is called on the cell. This is a great spot to reset your cells to their default state by removing any images or setting labels back to their default values. Next, tableView(_:willDisplay:forRowAt:) is called on the table views's delegate. This happens right before the cell is shown. You can perform some last-minute configuration here, but the majority of work should already be done in tableView(_:cellForRowAtIndexPath:). When the cell scrolls offscreen, tableView(_:didEndDisplaying:forRowAt:) is called on the delegate. This signals that a previously-visible cell has just scrolled out of the view's bounds.
With all this cell life cycle information in mind, the best way to fix the image-reuse bug is by implementing prepareForReuse() on ContactTableViewCell. Add the following implementation to remove any images that have previously been set:
override func prepareForReuse() { super.prepareForReuse() contactImage.image = nil }
Quite an easy fix for a pesky bug, don't you think? Let's have a look at another performance optimization that table views have, called prefetching.