While my initial answer, below, tried to solve several key issues related to asynchronous image retrieval, it is still mostly limited. A correct implementation also ensures that if you scroll quickly, the visible cells take precedence over the cells that scroll from the screen. It will also support canceling previous requests (and canceling them for you when necessary).
Although we could add these features to the code below, it's probably best to take an installed, tested solution that uses the NSOperationQueue and NSCache technologies discussed below, but also fixes the above issues. The easiest solution is to accept one of the installed UIImageView categories, which supports asynchronous image retrieval. The AFNetworking and SDWebImage libraries have UIImageView that gracefully handle all of these problems.
You can use NSOperationQueue or GCD for your lazy loading (see Concurrency Programming Guide for a discussion of various asynchronous operation technologies). The first advantage is that you can specify exactly how many simultaneous operations are permissible, which is very important when downloading images from the Internet, because many web servers limit the number of concurrent requests that they will receive from this client.
Main idea:
- Send a request for image data in a separate background queue;
- When the image download is complete, send the user interface update back to the main queue because you should never update user interfaces in the background;
- When you start sending the final user interface update code in the main queue, make sure that the
UITableViewCell is still displayed and that it has not been deleted and reused, because the corresponding cell scrolls from the screen. If you do not, an incorrect image may occur.
You would like to replace your code with something like the following code:
First, define a property for NSOperationQueue that you will use to load images, and also NSCache to save these images:
@property (nonatomic, strong) NSOperationQueue *imageDownloadingQueue; @property (nonatomic, strong) NSCache *imageCache;
Second, initialize this queue and cache in viewDidLoad :
- (void)viewDidLoad { [super viewDidLoad]; self.imageDownloadingQueue = [[NSOperationQueue alloc] init]; self.imageDownloadingQueue.maxConcurrentOperationCount = 4;
Thirdly, your cellForRowAtIndexPath might look like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { btnBack.hidden = FALSE; static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; cell.selectionStyle = UITableViewCellSelectionStyleNone; cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; cell.backgroundColor = [UIColor clearColor]; cell.textLabel.font = [UIFont fontWithName:@"Noteworthy" size:17.0]; cell.textLabel.font = [UIFont boldSystemFontOfSize:17.0]; cell.textLabel.textColor = [UIColor blackColor]; cell.textLabel.highlightedTextColor = [UIColor blackColor]; } cell.textLabel.text = [NSString stringWithFormat:@" %@", [test.arrTitle objectAtIndex:indexPath.row]];
Fourthly, and finally, although it would be possible to write code to clear the cache in situations with low memory, it turns out that it does this automatically, so no additional processing is required here. If you manually simulate the low memory situation in the simulator, you will not see that it crowds out its objects, because NSCache does not respond to UIApplicationDidReceiveMemoryWarningNotification , but during the actual operation, when the memory is low, the cache will be cleared. In fact, NSCache no longer elegantly responds to low memory situations, so you really need to add an observer for this notification and empty the cache in low memory situations.
I could offer many other optimizations (for example, perhaps also caching images in persistent storage to optimize future operations, I actually put all this logic in my own AsyncImage class), but first let's see if this solves the main performance problem.