tags:

views:

434

answers:

3

On Linux under X11 and using GTK+ you have something called "Main Loop". Once you start the main loop you have a timer that runs in the main thread of the application. You can set that timer to a callback function and you have a very nice application-wide timer.

This is sample code for that:

GMainLoop *loop;

if(!loop_running)
{
      display = XOpenDisplay( NULL );
      loop = g_main_loop_new(NULL, FALSE);
      g_timeout_add(1000, (GSourceFunc)callback, NULL); //1 sec laps   
      g_main_loop_run(loop);  //to stop use g_main_loop_quit ()  with the "loop" as arg
      loop_running=1;
}

I am trying to write a similar application for Mac OS X and instead of a main loop i am using a simple timer:

- (void) handleTimer: (NSTimer *) timer
{
    CopyDataToDB();
} // handleTimer

- (IBAction)startStopAction:(id)sender
{

   isOn=!isOn;
   if(isOn)
   {
       // Add our timers to the EventTracking loop       
       [[NSRunLoop currentRunLoop] addTimer: time forMode: NSEventTrackingRunLoopMode];

       // Add our timers to the ModelPanel loop       
       [[NSRunLoop currentRunLoop] addTimer: time forMode: NSModalPanelRunLoopMode];
   }
   else
   {
       [timer invalidate];
   }

}

That doesn't seem to work very well. The timer is set off all the time. I tried also with an NSTimer but no luck. I'm not very familiar with objective-c and specially with GUI applications.

Anyway, any ideas how to implement an application-wide timer on Cocoa (objective-c with Xcode)?

Thanks!

EDIT When using NSTimer this is the error I'm getting at runtime:

**[Session started at 2009-07-12 16:49:59 -0400.]
2009-07-12 16:50:02.784 MouseClick[1490:10b] Starting
2009-07-12 16:50:02.786 MouseClick[1490:10b] *** +[NSTimer scheduledTimerWithTimeInterval:selector:userInfo:repeats:]: unrecognized selector sent to class 0xa08d54c0
2009-07-12 16:50:02.787 MouseClick[1490:10b] *** +[NSTimer scheduledTimerWithTimeInterval:selector:userInfo:repeats:]: unrecognized selector sent to class 0xa08d54c0**

The Debugger has exited with status 0.

EDIT 2

ok, i got it. The problem was that i didn't add a "target:" to the timer. now, when I'm shutting the timer off I am getting the following error:

MouseClick(1652) malloc: * error for object 0x1645c0: double free * set a breakpoint in malloc_error_break to debug

The freeing of the timer is done as follow:

[timer invalidate]; 
[timer release]; 
timer = nil;
+1  A: 

A timer can be set up with any object as its target; and there are a number of suitable entry points for "attaching" a timer to the the desired target.

I'm not quite sure where you get time from in this context; but I'm guessing it's a timer created with +timerWithTimeInterval:invocation:repeats: (on NSTimer) perhaps? If not, that particular method should hand you a very useful timer for your use.

Williham Totland
+5  A: 

Depends on what exactly you are trying to do. Most of the time you should not mess with the run loop and simply set up a timer:

const float framerate = 40;
const float frequency = 1.0f/framerate;
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:frequency
    target:self selector:@selector(doSomething)
    userInfo:nil repeats:YES];

Now the doSomething method is going to get executed about 40 times per second. If you want to execute something as often as possible, you can spawn a new thread:

- (void) loop
{
    while (running)
    {
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
        // Do something useful.
        [pool release];
    }
}

- (void) run
{
    run = YES;
    [NSThread detachNewThreadSelector:@selector(loop)
        toTarget:self withObject:nil];
}

Of course now you have threads, which means synchronizations and more headache than you would have with timers. As I said, it depends on what you want to do.

And to answer your original question: Yes, Cocoa applications do have a “main loop” that’s probably similar to the GTK one. As the loop is already created for you, there is no point in writing another one – unless you are trying to figure out how things work or do something sophisticated. See Run Loop Management in Threading Programming Guide for details.

If you want to simply handle events, the default run loop will do it for you. Just implement the handlers, ie. methods connected to buttons and such stuff. If you want to do something periodically (like update an animation or so), set up a timer (NSTimer). The default run loop will take care of the time and it will call the appropriate selector as often as you want to.

zoul
ok, i tried something similar but i was getting a lot of errors. This is the code (sorry for being not formatted it doesn't allow me to format it on a comment):timer = [NSTimer scheduledTimerWithTimeInterval: 0.5 selector: @selector(handleTimer:) userInfo: nil repeats: YES];
wonderer
This should work. Could you add the code and the errors you are getting to the question?
zoul
See in the question I added the error messages
wonderer
ok, i got it. The problem was that i didn't add a "target:" to the timer.now, when I'm shutting the timer off I am getting the following error:MouseClick(1652) malloc: *** error for object 0x1645c0: double free*** set a breakpoint in malloc_error_break to debugThe freeing of the timer is done as follow: [timer invalidate]; [timer release]; timer = nil;
wonderer
Just send the ‘invalidate’ message and leave out the ‘release’. Running timers are not released by hand, you call ‘invalidate’ and the run loop will release the timer some time later. (See the NSTimer documentation.)
zoul
Thanks! I'm going to try to pack all the functions into this one file with the timer. Hopefully this will work.
wonderer
A: 

When trying NSTimer, did you retain the timer? What about retaining the object the timer calls as well...

Kendall Helmstetter Gelner
The timer gets retained by the run loop, you don’t have to do it yourself.
zoul