Cell Data Unbinding

Recently a user reported a bug of Moke:

Some pictures don't show if I scroll up immediately after scrolling down in a user album.

Fortunately I can reproduce it. After several hours' debugging I finally found the cause: cellForItemAtIndexPath is not called for those UICollectionViewCells before they are displayed, i.e. before willDisplayCell is called.

The missing of cellForItemAtIndexPath calls doesn't occur usually, of course. Otherwise, all apps using UICollectionView will break.

It only occurs for those cells that disappear and then immediately appear again—the user scrolls them out of bounds and then scrolls them back into bounds again. cellForItemAtIndexPath is correctly called when they first appear.

Though this behavior was unexpected to me—that's why bugs happen—it is totally reasonable: cellForItemAtIndexPath is for binding data to cell. If a cell's indexPath doesn't change its corresponding data doesn't change—assuming the data source doesn't change—it can be reused as is without data rebinding.

Most apps won't be affected by this behavior because they only do data binding not data unbinding. But my app does data unbinding, out of my excessive care about memory usage.

The place I do data unbinding is didEndDisplayingCell. Without the expected cellForItemAtIndexPath call those cells don't have the chance to rebind to their data, hence show up empty.

Though this behavior currently only occurs for UICollectionView. You should also take it into account when using UITableView.

Conclusion:

  • didEndDisplayingCell pairs with willDisplayCell not cellForItemAtIndexPath or cellForRowAtIndexPath.

  • willDisplayCell may be called without calling cellForItemAtIndexPath or cellForRowAtIndexPath first. The cell may be scrolled back into bounds immediately after being scrolled out of bounds while user is scrolling back and forth. In this situation willDisplayCell may be called after didEndDisplayingCell without cellForItemAtIndexPath or cellForRowAtIndexPath in between.

  • Data unbinding can be safely done in didEndDisplayingCell only if data binding is done in willDisplayCell. But normally data binding is done in cellForItemAtIndexPath or cellForRowAtIndexPath so there is no safe and convenient place to do data unbinding.