6
votes

I am downloading images in table view cells as they scroll onto the screen. For UX reasons, I start downloading the images in - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath. I do not wait until the table view is done scrolling. When the table view cell is set, I start downloading images that I do not have already. However, they do not seem to fully download until the table view stops moving. As soon as it stops moving, the images almost download instantly.

Is there anyway to use NSURLConnection where it is not blocked by the main UI thread? Or, is there a way where these images will download very quickly while the table view is being scrolled.

** EDIT **

To prove that NSURLConnection is slower I used NSThread to detach a new selector in a different thread. I then download the data and call back to the main thread where I create a UIImage and show it in the table view. This method works MUCH faster.

Personally, I think that NSURLConnection is getting thrown into the event loop where the UITableView scrolling is blocking it.

4
A user is able to scroll a table much faster than the phone is able to hit the network and download the image. Sounds normal to me...psychotik
I just implemented my same solution by detaching a new NSThread and it is downloading MUCH faster.rickharrison
Definitely the download makes use of the event loop. It sounds plausible that the scrolling is somehow shutting it out.JeremyP

4 Answers

22
votes

Read NSDefaultRunLoopMode vs NSRunLoopCommonModes for a good explanation of why all the download delegate notifications are queued up, but to download while scrolling when using the main thread change from this:

NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request
                                                              delegate:self];

to this:

NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request
                                                              delegate:self
                                                      startImmediately:NO];
[connection scheduleInRunLoop:[NSRunLoop currentRunLoop]
                      forMode:NSRunLoopCommonModes];
[connection start];
2
votes

I have experienced this problem before. The asynchronous delegate methods of NSURLConnection are not firing while a scrollView is being scrolled. Although the downloading works in the background, your main thread is not informed of new images. Just like you, I believe the problem is related to scrollviews being scrolled in an inner NSRunLoop with a different RunLoopMode. I have been talking to Apple staff about this and have them look at my code, but we couldn't work out a solution.

On the other hand Jeff LaMarche has this post on his blog, where he does the same thing, and it works as expected. I have not been able to figure out what he does differently (mainly due to not having time), but this may be worth a look.

1
votes

If you mean "does NSURLConnection execute on the main thread?", then yes, I believe that's the case. The connection is opened and the delegate methods are executed on the main thread. I haven't found any documentation to suggest otherwise, and you can verify it by debugging.

I think your supposition that the UITableView scrolling blocks the NSURLConnection callbacks in the main run loop is correct.

You've already posted one solution, spawning a thread for your selector. Another alternative would be to execute your downloads as NSOperations, which has a couple of benefits:

  • If you force the operations to run concurrently (see Dave Dribin's excellent post on this), you can limit the number of simultaneous downloads, which might be desirable if you have a very large number of images in your table. You say your downloads occur "almost instantly", but that may not be the case if your user is on a slow connection and your table contains a lot of images.
  • You can cancel all the operations if the user does something that makes the image downloads irrelevant, like executing another search.

Dave Dribin's approach, which I use, forces the connections to execute back on the main thread, but that probably isn't necessary for your purposes--you could use your current approach of calling back to the main thread after your images download.