I have a UITableView that displays images associated with contacts in each row. In some cases these images are read on first display from the address book contact image, and where there isn’t one they are an avatar rendered based on stored data. I presently have these images being updated on a background thread using GCD. However, this loads the images in the order they were requested, which means during rapid scrolling the queue becomes lengthy and when the user stops scrolling the current cells are the last to get updated. On the iPhone 4, the problem isn’t really noticeable, but I am keen to support older hardware and am testing on an iPhone 3G. The delay is tolerable but quite noticeable.
It strikes me that a Last In-First Out stack would seem likely to largely resolve this issue, as whenever the user stopped scrolling those cells would be the next to be updated and then the others that are currently off-screen would be updated. Is such a thing possible with Grand Central Dispatch? Or not too onerous to implement some other way?
Note, by the way, that I am using Core Data with a SQLite store and I am not using an NSFetchedResultsController because of a many-to-many relationship that has to be traversed in order to load the data for this view. (As far as I am aware, that precludes using an NSFetchedResultsController.) [I’ve discovered an NSFetchedResultsController can be used with many-to-many relationships, despite what the official documentation appears to say. But I’m not using one in this context, yet.]
Addition: Just to note that while the topic is “How do I create a Last In-First Out Stack with GCD”, in reality I just want to solve the issue outlined above and there may be a better way to do it. I am more than open to suggestions like timthetoolman’s one that solves the problem outlined in another way; if such a suggestion is finally what I use I’ll recognize both the best answer to the original question as well as the best solution I ended up implementing… 🙂
The code below creates a flexible last in-first out stack that is processed in the background using Grand Central Dispatch. The SYNStackController class is generic and reusable but this example also provides the code for the use case identified in the question, rendering table cell images asynchronously, and ensuring that when rapid scrolling stops, the currently displayed cells are the next to be updated.
Kudos to Ben M. whose answer to this question provided the initial code on which this was based. (His answer also provides code you can use to test the stack.) The implementation provided here does not require ARC, and uses solely Grand Central Dispatch rather than performSelectorInBackground. The code below also stores a reference to the current cell using objc_setAssociatedObject that will enable the rendered image to be associated with the correct cell, when the image is subsequently loaded asynchronously. Without this code, images rendered for previous contacts will incorrectly be inserted into reused cells even though they are now displaying a different contact.
I’ve awarded the bounty to Ben M. but am marking this as the accepted answer as this code is more fully worked through.
SYNStackController.h
SYNStackController.m
In the view.h, before @interface:
In the view.h @interface section:
In the view.h, after the @interface section:
In the view.m, before @implementation:
In the view.m viewDidLoad:
In the view.m: