2
votes

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

2

2 Answers

6
votes

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.

2
votes

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.