/** Implementation of NSTimer for GNUstep Copyright (C) 1995, 1996, 1999 Free Software Foundation, Inc. Written by: Andrew Kachites McCallum Created: March 1996 Rewrite by: Richard Frith-Macdonald This file is part of the GNUstep Base Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA. NSTimer class reference $Date$ $Revision$ */ #import "common.h" #define EXPOSE_NSTimer_IVARS 1 #import "Foundation/NSTimer.h" #import "Foundation/NSDate.h" #import "Foundation/NSException.h" #import "Foundation/NSRunLoop.h" #import "Foundation/NSInvocation.h" @class NSGDate; @interface NSGDate : NSObject // Help the compiler @end static Class NSDate_class; /** *

An NSTimer provides a way to send a message at some time in * the future, possibly repeating every time a fixed interval has passed. To * use a timer, you can either create one that will automatically be added to * the run loop in the current thread (using the -addTimer:forMode: method), * or you can create it without adding it then add it to an [NSRunLoop] * explicitly later. *

*

NB. You may not use -init or +new to create a timer, as the timer must * be properly initialised to send an action after some interval. *

*/ @implementation NSTimer + (void) initialize { if (self == [NSTimer class]) { NSDate_class = [NSGDate class]; } } /* For MacOS-X compatibility, this returns nil. */ - (id) init { DESTROY(self); return nil; } /** * Initialise the receive, a newly allocated NSTimer object.
* The ti argument specifies the time (in seconds) between the firing. * If it is less than or equal to 0.0 then a small interval is chosen * automatically.
* The fd argument specifies an initial fire date copied by the timer... * if it is not supplied (a nil object) then the ti argument is used to * create a start date relative to the current time.
* The f argument specifies whether the timer will fire repeatedly * or just once.
* If the selector argument is zero, then then object is an invocation * to be used when the timer fires. otherwise, the object is sent the * message specified by the selector and with the timer as an argument.
* The object and info arguments will be retained until the timer is * invalidated. */ - (id) initWithFireDate: (NSDate*)fd interval: (NSTimeInterval)ti target: (id)object selector: (SEL)selector userInfo: (id)info repeats: (BOOL)f { if (ti <= 0.0) { ti = 0.0001; } if (fd == nil) { _date = [[NSDate_class allocWithZone: NSDefaultMallocZone()] initWithTimeIntervalSinceNow: ti]; } else { _date = [fd copyWithZone: NSDefaultMallocZone()]; } _target = RETAIN(object); _selector = selector; _info = RETAIN(info); if (f == YES) { _repeats = YES; _interval = ti; } else { _repeats = NO; _interval = 0.0; } return self; } /** * Create a timer which will fire after ti seconds and, if f is YES, * every ti seconds thereafter. On firing, invocation will be performed.
* NB. To make the timer operate, you must add it to a run loop. */ + (NSTimer*) timerWithTimeInterval: (NSTimeInterval)ti invocation: (NSInvocation*)invocation repeats: (BOOL)f { return AUTORELEASE([[self alloc] initWithFireDate: nil interval: ti target: invocation selector: NULL userInfo: nil repeats: f]); } /** * Create a timer which will fire after ti seconds and, if f is YES, * every ti seconds thereafter. On firing, the target object will be * sent a message specified by selector and with the timer as its * argument.
* NB. To make the timer operate, you must add it to a run loop. */ + (NSTimer*) timerWithTimeInterval: (NSTimeInterval)ti target: (id)object selector: (SEL)selector userInfo: (id)info repeats: (BOOL)f { return AUTORELEASE([[self alloc] initWithFireDate: nil interval: ti target: object selector: selector userInfo: info repeats: f]); } /** * Create a timer which will fire after ti seconds and, if f is YES, * every ti seconds thereafter. On firing, invocation will be performed.
* This timer will automatically be added to the current run loop and * will fire in the default run loop mode. */ + (NSTimer*) scheduledTimerWithTimeInterval: (NSTimeInterval)ti invocation: (NSInvocation*)invocation repeats: (BOOL)f { id t = [[self alloc] initWithFireDate: nil interval: ti target: invocation selector: NULL userInfo: nil repeats: f]; [[NSRunLoop currentRunLoop] addTimer: t forMode: NSDefaultRunLoopMode]; RELEASE(t); return t; } /** * Create a timer which will fire after ti seconds and, if f is YES, * every ti seconds thereafter. On firing, the target object will be * sent a message specified by selector and with the timer as its * argument.
* This timer will automatically be added to the current run loop and * will fire in the default run loop mode. */ + (NSTimer*) scheduledTimerWithTimeInterval: (NSTimeInterval)ti target: (id)object selector: (SEL)selector userInfo: (id)info repeats: (BOOL)f { id t = [[self alloc] initWithFireDate: nil interval: ti target: object selector: selector userInfo: info repeats: f]; [[NSRunLoop currentRunLoop] addTimer: t forMode: NSDefaultRunLoopMode]; RELEASE(t); return t; } - (void) dealloc { if (_invalidated == NO) { [self invalidate]; } RELEASE(_date); [super dealloc]; } /** * Fires the timer ... either performs an invocation or sends a message * to a target object, depending on how the timer was set up.
* If the timer is not set to repeat, it is automatically invalidated.
* Exceptions raised during firing of the timer are caught and logged. */ - (void) fire { id target; /* We retain the target so it won't be deallocated while we are using it * (if this timer gets invalidated while we are firing). */ target = [_target retain]; /* We check that we have not been invalidated before we fire. */ if (NO == _invalidated) { if (_selector == 0) { NS_DURING { [(NSInvocation*)target invoke]; } NS_HANDLER { NSLog(@"*** NSTimer ignoring exception '%@' (reason '%@') " @"raised during posting of timer with target %p " @"and selector '%@'", [localException name], [localException reason], target, NSStringFromSelector([target selector])); } NS_ENDHANDLER } else { NS_DURING { [target performSelector: _selector withObject: self]; } NS_HANDLER { NSLog(@"*** NSTimer ignoring exception '%@' (reason '%@') " @"raised during posting of timer with target %p and " @"selector '%@'", [localException name], [localException reason], target, NSStringFromSelector(_selector)); } NS_ENDHANDLER } } [target release]; if (_repeats == NO) { [self invalidate]; } } /** * Marks the timer as invalid, causing its target/invocation and user info * objects to be released.
* Invalidated timers are automatically removed from the run loop when it * detects them. */ - (void) invalidate { /* OPENSTEP allows this method to be called multiple times. */ _invalidated = YES; if (_target != nil) { DESTROY(_target); } if (_info != nil) { DESTROY(_info); } } /** * Checks to see if the timer has been invalidated. */ - (BOOL) isValid { if (_invalidated == NO) { return YES; } else { return NO; } } /** * Returns the date/time at which the timer is next due to fire. */ - (NSDate*) fireDate { return _date; } /** * Change the fire date for the receiver.
* NB. You should NOT use this method for a timer which has * been added to a run loop. The only time when it is safe to modify * the fire date of a timer in a run loop is for a repeating timer * when the timer is actually in the process of firing. */ - (void) setFireDate: (NSDate*)fireDate { ASSIGN(_date, fireDate); } /** * Returns the interval between firings, or zero if the timer * does not repeat. */ - (NSTimeInterval) timeInterval { return _interval; } /** * Returns the user info which was set for the timer when it was created, * or nil if none was set or the timer is invalid. */ - (id) userInfo { return _info; } /** * Compares timers based on the date at which they should next fire. */ - (NSComparisonResult) compare: (id)anotherTimer { if (anotherTimer == self) { return NSOrderedSame; } else if (anotherTimer == nil) { [NSException raise: NSInvalidArgumentException format: @"nil argument for compare:"]; } else { return [_date compare: ((NSTimer*)anotherTimer)->_date]; } return 0; } @end