Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do I need an NSRunLoop to run a timer?

I bought the Big Nerd Ranch Guide for Objective-C, and there is something about NSRunLoop I can't figure out.

Here's a chunk of code from the book:

NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 
                                                  target:logger
                                                selector:@selector(updateLastTime:)
                                                userInfo:nil
                                                 repeats:YES];
[[NSRunLoop currentRunLoop] run];

My question is, why do I need to put an NSRunLoop for the NSTimer object to be processed? And why does it need to be at the end, and not the beginning?

Why it is not like the other functions or object's methods where I simply have to call a function for it to be processed and logged into the console?

I'm really trying figure out every logic of every detail here.

like image 493
ocampeau Avatar asked Nov 17 '25 05:11

ocampeau


1 Answers

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.)

like image 112
Rob Napier Avatar answered Nov 18 '25 19:11

Rob Napier



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!