0

I'm making a CLI tool for Mac OS X, my main looks something like this:

int main(int argc, const char * argv[]){

    @autoreleasepool {

        //Some startup stuff here
        [ServerPool sharedPool]; //Start the pool

        /*
         UI stuff for cli version
         */
        while (1){
            [[CLIUI sharedCLI] mainMenu];
            [[CLIUI sharedCLI] menuInput];
        }

    }
    return 0;
}

and in another section of the code I am starting a NSTimer

progressUpdateTimer = [NSTimer timerWithTimeInterval:1.0
                                              target:self
                                            selector:@selector(updateProgress:)
                                            userInfo:nil
                                             repeats:YES];

[[NSRunLoop currentRunLoop] addTimer: progressUpdateTimer forMode:NSDefaultRunLoopMode];

However, it never fires the selector updateProgress:. I believe this is because I do not have an NSRunLoop established in my main, what would be the proper fix for this?

Ryan Copley
  • 873
  • 1
  • 10
  • 26

1 Answers1

1

Timers and anything else dependent on the run loop won't fire unless the run loop is running.

In a command line program, you'd ideally use dispatch_main() and pass control to GCD or you run the [NSThread currentRunLoop] in an appropriate mode for an appropriate amount of time.

You're gonna have to get rid of that while(1) as it'll block the run loop.

The concurrent programming documentation has extensive documentation on how to use run loops.

If you really need that while loop, move it to a second thread (an NSRunLoop on the main thread is a lot easier to deal with) and run the run loop on the main thread.


You'll need to replace that while(1) loop with an NSRunLoop. Which also likely means that you'll need to change your input code to be either a dispatch_source or an NSFileHandle that is reading from stdin, etc...

I.e. you are going to have to refactor your code quite a bit. Do a google search for something like "using nsrunloop in a command line program" and you'll likely get quite a few hints. As well, the NSRunLoop documentation points to a bunch of examples.

It may seem daunting, but once you grok NSRunLoops they are amazingly powerful constructs.

bbum
  • 162,346
  • 23
  • 271
  • 359
  • Could you elaborate here? I'm completely oblivious to how (or where) my main loop should do/go... – Ryan Copley Jun 01 '13 at 21:59
  • I have switched up to this: https://gist.github.com/RyanCopley/829d33bb328651b1fb1b Everything seems to work, except the timers are still not firing. – Ryan Copley Jun 01 '13 at 22:53
  • What does your `sharedCLI` method do? If it is blocking, waiting for input, then the timers won't fire (which is why you'll want to move to NSFileHandle or move the existing I/O off the main thread). – bbum Jun 01 '13 at 23:34
  • It just sets up the singleton and calls the init function, which I put in the gist comments. I put my blocking calls on an async thread to no avail. =/ I even switched to a NSFileHandle to read the stdin and it still blocks, even on the async thread. – Ryan Copley Jun 02 '13 at 00:06
  • OK -- take out the call to your init function and just fire the timer. Does it fire? If so, your run loop is configured properly, and something is blocking it. If not, your run loop is configured properly. – bbum Jun 02 '13 at 02:56
  • OH. I didn't understand the concept of an NSRunLoop at all, now this makes sense. the [NSRunLoop runMode:beforeDate] is the entry point when it accepts the ability to run the timers! I didn't know what this function did at all. – Ryan Copley Jun 02 '13 at 23:45
  • @RyanCopley Ah. Sorry -- Noted for future explanations. Right; you schedule stuff in the run loop and they will trigger at the appropriate times or based on the appropriate events when appropriate *as long as the loop is running*. Note that a run loop does not poll; it will use 0% CPU when there are no events happening! Quite elegant, really! – bbum Jun 03 '13 at 00:23