4

I've noticed that if I create an NSURLConnection and fire the request, all is well. My delegate methods get called and the last delegate method get's called well after the code block invoking the connection completes. Great.

That leads me to believe the connections are asynchronous which implies that they're multi-threaded. Is that correct? Could they be asynchronous but in the same thread? No, that's crazy - right?

But, in every example I've seen using an NSOperation, NSURLConnections are always scheduledInRunLoop after which [runLoop runMode ...] is invoked in a while loop.

Can someone explain exactly what is happening here? It seems to me that the first case requires spawning secondary threads but no manual invocation of the run loop (on those threads) while NSOperation (in a new thread) does require manual invocation of the run loop.

Why is no manual invocation required for the first case?

Luther Baker
  • 7,236
  • 13
  • 46
  • 64

3 Answers3

4

NSURLConnection does spawn a single background thread to manage all instances of itself, but this is generally irrelevant to the caller, since the delegate calls are made on whatever thread owns the runloop the connection was scheduled in. (This fact turned out to be very relevant to me recently, but these things really only come up when dealing with insane crashers in multi-threaded apps.)

For more caller-relevant details, you should look at the docs for -[NSURLConnection scheduleInRunLoop:forMode:]. It explains how to manually handle scheduling and unscheduling, and how NSURLConnections behave in a multi-threaded environment.

If you are unclear on how run loops work and how they perform asynchronous actions without requiring additional threads, you should read Run Loops in the Threading Programming Guide. This is a very important topic for moving to more advanced Cocoa development.

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • (delegate calls are made on whatever thread owns the runloop the connection was scheduled in) ... are you distinguishing calls to the delegate from calls to NSURLConnection? – Luther Baker Mar 03 '11 at 22:50
  • 1
    I'm in the docs - but still not clear why NSURLConnection needs to be attached to the run loop in some instances and not others. Is there a page I'm missing? I'm gathering that it ALWAYS needs a run loop and that when you fire it up from main, it uses mains. What throws me is that fact that even from main, NSURLConnection spawns a secondary thread ... and by definition, while that thread _has_ a run loop, it will NOT be running - so, why does it work? (from main that is) – Luther Baker Mar 03 '11 at 22:53
  • 1
    Of course. Calls to NSURLConnection happen on whatever thread your code calls them on. Calls *from* NSURLConnection are what the framework controls. – Rob Napier Mar 03 '11 at 22:55
  • 1
    It does always have to be attached to a runloop. It just schedules itself when you call -initWithRequest:delegate:. The secondary thread has nothing to do with you; it's used internally to handle resource loading. You should ignore it. – Rob Napier Mar 03 '11 at 22:58
  • BTW, NSURLConnection does run the RunLoop on its private thread. You can see it in stack traces. That's not our business, but it definitely manages its own runloop internally. – Rob Napier Mar 03 '11 at 22:59
  • 1
    Ah ... so calls _from_ NSURLConnection happen in the automatically spawned secondary thread while callbacks _to_ the delegate fire in the main thread - or whatever thread you created NSURLConnection in (which consequently, needs a RunLoop to pull those messages off)! – Luther Baker Mar 03 '11 at 23:01
  • 1
    Certain NSURLConnection-internal calls are made on that secondary thread. Calls from NSURLConnection to the delegate are processed by whatever runloop(s) NSURLConnection is scheduled on (which may not be the one it was created on, but is by default). It is legal to schedule NSURLConnection on multiple runloops at the same time, in which case you can get duplicates of your delegate methods on multiple threads (this is considered a feature). None of your calls, whether to or from NSURLConnection, will happen on the private thread. – Rob Napier Mar 03 '11 at 23:08
  • Would 5 serial invocations of initWithRequest:delegate spawn 5 threads or 1 background thread to manage all of their resources .. such that, as responses came back, they'd show up on the runloop thread at which time they'd be picked up and passed to the delegate - handled serially, one message at a time. – Luther Baker Mar 03 '11 at 23:11
  • 1 background thread. I have never seen NSURLConnection spawn a second one, and once it spawns its thread, it seems to keep it around for a long time (possibly forever). This is an internal implementation detail, but it is useful information since it means that you won't spawn a ton of expensive threads. But the number of private threads is irrelevant to the callbacks. Callbacks run on the scheduled runloop, which is not its private thread (yes, it does private work on that thread, but that's not what it means by being scheduled; it means scheduleInRunLoop:forMode: was called). – Rob Napier Mar 03 '11 at 23:21
  • 1
    That certainly helps me understand the term 'asynchronous'. My take away is that, while NSURLConnections are asynchronous, it is not via naive 1:1 mapping of request to thread. The asynchronous aspect is largely bcs the process is event driven - and these events happen and get picked up by the run loop and delivered - much like mouse movement gets picked up and delivered - as they come in, not on some spawned thread. Technically, as I allude to in the title of this post, NSURLConnections are asynchronous but not by-way-of multiple threads, they just happen to use one as an impl detail. – Luther Baker Mar 03 '11 at 23:37
  • I have seen, and can easily reproduce NSURLConnection spawning more than 1 background thread, although definitely not one per thread. My guess is that it is related to the number of cores. – hooleyhoop Mar 04 '11 at 09:36
0

If you want to run NSURLConnection in another thread, you should create a run loop like this in your thread's main method:

while (!finished)
{
  [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
}
Richard
  • 339
  • 3
  • 10
0

Because the main thread already has a run loop, I'd imagine.

Caleb
  • 124,013
  • 19
  • 183
  • 272