From the early days of what would become Cocoa, when dinosaurs roamed the Earth, rocks were soft, and NeXT workstations were new, up until 10.6 came out, the most common type of multitasking was the run loop. It's cooperative multitasking. There are no threads. There is no preemptive scheduler or kernel. There are no context switches. There's just a big run loop that says "what needs doing now?" and runs it. And when that thing completes, it waits for the next thing that needs doing and runs that. It literally is a big while(true)
loop. Well, technically the line of code is:
for (;;) { ... }
You can see for yourself in CFRunLoop.c. Look for __CFRunLoopRun
.
NSTimer
was invented in those days. All it does it make a note in the runloop telling it "when this time passes, then please do this." (It's a tiny bit more complicated than that because it uses mach ports, look for __CFRunLoopTimerSchedule
in the same file for details, but basically that's the idea.)
So the point is, there's no magic. There's just a big for(;;)
loop that processes this stuff. Something has to run it. And when you start it (with run
), it doesn't return. It's an infinite loop. There is no "background." There are no other threads. And that's why you need to do things in the order BNR tells you to. Otherwise your next line of code wouldn't run.
Of course in iOS apps and OS X GUI apps, you don't usually have to do this yourself. The run loop gets created for you during program startup, and the whole main thread lives inside of it. It's the thing that calls you most of the time. You don't call it. But if you're on a thread other than the main thread, and you want to use run loop functionality, you're going to have to run it yourself.
Today, a lot of things are done with GCD rather than run loops. That's the "until 10.6 came out" that I mentioned. It really changed the Cocoa world. But a huge amount of Cocoa still relies on the run loop, and it's still the workhorse of most apps even if you never think about it.
In most cases today, if you're having to create a runloop in order to use NSTimer
, you shouldn't be using NSTimer
. Just use dispatch_after
. In fact, that's what I usually recommend most of the time today even if you do have a runloop.
(And you should definitely read the link @quelish gives in the comments. It is the definitive word on run loops.)