The right way to load content in background thread with TableView

on

Blog_3

UITableView is extremely useful class to list content which Apple provide to iOS developer. UITableView use “reusable” pattern to handle content in each cell. So it can contain 100+ row with good performance.

There are many common scenarios when you deal with UITableView.

One of this is load asynchronous content in cell.

Like this

But, in this code have some issues memory.

Image you have 100 row, each of row you must load async big UIImage. So before cell is being displayed. TableView will call “Data Source” which provide cell. And in cellForRowAtIndexPath: will fire async method to load content.

But if this cell is OUT of visible area. The async operation you called is still doing work. So when this operation update UI in main thread, this UIImage is unnecessary resource. Sometime, it cause some “buggy”.

UITableViewCells are often reused instances. This means that cells being loaded into the view may sometimes contain data that was loaded originally into a completely different cell.

So, to handle this common scenarios. I use NSOperation and NSOperationQueue  Apple was introduced NSOperationQueue and many relative class in iOS 4. This was build in top of Grand Dispatch Central. But improve more enhancement. NSOperation can be cancel and resume easily.

In brieft, we will implement step-by-step :

  1. Init array or dictionary
  2. In cellForRowAtIndexPath:, we will create NSOperationBlock and add to background thread. You must implement how it do if this operation is NOT cancel. Finally, we add to array or dictionary to usable.
  3. Implement didEndDisplayingCell, this method will be called if cell is OUT of visible area.
  4. Get operation in arr / dictionary and cancel. And remove out.

Here is sample :

and we take advantage in didEndDisplayngCell method.

We will cancel operation which called in cellForRowAtIndexPath: before.

So, this is the best way to handle this scenarios. You can cancel all of operation if you push to new UIViewController.

Finally, you can improve more performance by setting setMaxConcurrentOperationCount.

 

Feel free for giving me your comment or your opinion.

Thanks for reading ;]

3 thoughts on “The right way to load content in background thread with TableView

  1. Amazingly helpful and informative post. You have helped me achieve 60fps scrolling!

    I think there may possibly be an edge-case bug which could happen if the containing UIViewController ever gets deallocated. I’ve provided my solution after the explanation.

    If the background thread is the last to store a strong reference to the view controller, then when it releases it, -dealloc will get called on a background thread, which is problematic for UIKit classes.
    Inside the second nested block added to [NSOperation mainQueue], there is a reference to the direct instance variable ‘_myDictionary’. This would cause the outer block, which is on a background thread, to store a strong reference to self (the owner of the instance variable). This could then cause ‘the deallocation problem’ as mentioned previously.

    By storing the value of the instance variable and using that within the block, a strong reference will be made to the variable instead of self, avoiding retaining self. Example:

    NSMutableDictionary *blockMyDictionary = _myDictionary; // outside block

    [blockMyDictionary removeObjectForKey:indexPath]; // inside block

    Let me know if you agree or not, and thanks again for the great post. Here are some links I have based my thoughts on.
    1. ‘The Deallocation Problem’ section
    https://developer.apple.com/library/content/technotes/tn2109/_index.html#//apple_ref/doc/uid/DTS40010274-CH1-SUBSECTION11
    2. ‘Object and Block Variables’ section on instance variable access by reference/value
    https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Blocks/Articles/bxVariables.html

Leave a Reply

Your email address will not be published. Required fields are marked *