views:

980

answers:

2

Hi everybody,

I have recently been learning the Apple SDK (for iPhone, etc) and came across something I can't understand. In the docs for "Using NSURLConnection" from http://developer.apple.com/documentation/Cocoa/Conceptual/URLLoadingSystem/Tasks/UsingNSURLConnection.html

I found a strange piece of explanation and example code. First, it says:

The download starts immediately upon receiving the initWithRequest:delegate: message. It can be canceled any time before the delegate receives a connectionDidFinishLoading: or connection:didFailWithError: message by sending the connection a cancel message.

Next, it shows the following piece of code:

  NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];

  if (theConnection) {

    // Create the NSMutableData that will hold

    // the received data

    // receivedData is declared as a method instance elsewhere

    receivedData=[[NSMutableData data] retain];

  } else {

    // inform the user that the download could not be made

  }

So, it seems to me that the download must start immediately in a different thread as soon as theConnection is initialized. This is clear because the code is non blocking and sends back messages to the delegate, in this case, self. Yet, the (autorelease style) allocation of receivedData happens after the other thread is started. Isn't this an unsafe race condition? Couldn't this result in a crash, memory leak, or loss of data in the event of a very fast server response (e.g. over loopback device) or in the case of unlucky thread scheduling? Wouldn't it make more sense to allocate receivedData before theConnection is initialized, and then just release it in the else case above?

I am so confused by this piece of code, hope somebody can shed some light on it for me. Thanks for any info,

Rudi Cilibrasi

+6  A: 

There is no race condition. The download is started in the background on a separate thread, but the messages sent to the delegate to inform you of the download progress are always called on the thread that started the download.

You can certainly allocate the NSData before the connection is created, if that's more clear to you. You could even allocate it in the connection:didReceiveData: method, if you wanted to make sure that the NSData didn't get allocated unless there was data to be stored.

I think the example is written the way it is to make it as short as possible, so as not to confuse the presentation with a lot of irrelevant code.

From the documentation for NSURLConnection:

NSURLConnection’s delegate methods allow an object to receive informational callbacks about the asynchronous load of a URL request. Other delegate methods provide facilities that allow the delegate to customize the process of performing an asynchronous URL load.

Note that these delegate methods will be called on the thread that started the asynchronous load operation for the associated NSURLConnection object.

Mark Bessey
That makes sense. Can you give a URL that supports the claim that all delegation messages get sent through the main thread please for my study? Thanks for the help and info.
Also of interest is that NSURLConnection's delegation messages are marshaled through the run-loop and thus can only occur once control is returned to it.
rpetrich
Thanks very much for the help Mark Bessey and rpetrich. This is crystal clear to me now and I do appreciate it.
+1  A: 

There is no race condition here. NSURLConnection uses the NSRunLoop to dispatch its events. Therefore no data can arrive to you until the next event loop begins.

This means no calls to connection:didReceiveData: will occur until the next event loop, no matter when the data actually comes back, and that connection:didReceiveData: will be called on the thread that started the load. So you have the rest of this run loop to get everything in order. "Immediately" means here "you don't need to do anything to start it."

This isn't conjecture or a changeable implementation detail; it's based on Cocoa's design principles. To best understand Cocoa, assume that almost everything occurs on the main thread. While the frameworks may occasionally spawn threads as an implementation detail, they will always provide the illusion that they do not. Asynchronous operations, by their nature, will therefore always show up on a later event loop. Cooperative, not preemtive, multitasking is the Cocoa way.

Rob Napier
That'll teach me to leave my browser window open so long before actually responding to something.... Yes, I've just completely repeated Mark, who has the correct answer.
Rob Napier
Upvoted, since you added some additional detail about the NSRunLoop that I didn't quite get.
Mark Bessey