TargetOwnedTimer.m [plain text]
/*
* TargetOwnedTimer.m
*
* $Header$
*
* Copyright 2004 Massachusetts Institute of Technology.
* All Rights Reserved.
*
* Export of this software from the United States of America may
* require a specific license from the United States Government.
* It is the responsibility of any person or organization contemplating
* export to obtain such a license before exporting.
*
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
* distribute this software and its documentation for any purpose and
* without fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright notice and
* this permission notice appear in supporting documentation, and that
* the name of M.I.T. not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission. Furthermore if you modify this software you must label
* your software as modified software and not distribute it in such a
* fashion that it might be confused with the original M.I.T. software.
* M.I.T. makes no representations about the suitability of
* this software for any purpose. It is provided "as is" without express
* or implied warranty.
*/
#import "TargetOwnedTimer.h"
// Kerberos.app has a bunch of objects which are created by the main controller
// (either directly or indirectly) and released when the controller no longer
// needs them. Some of these objects want NSTimers -- and they want the NSTimer
// invalidated when they are released by the controller.
//
// Unfortunately, NSTimers retain their targets. As a result, if the objects were
// directly instantiated the timers, they would never get freed when released
// since the timer retained the object. Yay for circular dependencies created
// by refcounted objects! Woooo!
//
// This isn't inherently wrong. Owning the object you plan to call back into is
// safer because you know it will exist when you call into it. And depending on
// how your application works, you might actually want your objects to persist
// until the timer fires and then be released.
//
// Obviously we could create invalidate methods for every Kerberos object that needs
// to create a timer and then call the timer's invalidate method from there, but
// that sort of defeats the usefulness of having refcounted objects in the first
// place. And it's prone to simple logic errors which cause silent memory leaks
// since a number of the aforementioned objects are in NSArrays and are only
// freed as a side effect of being removed from the array.
//
// Instead, here is a TargetOwnedTimer class which encapsulates an NSTimer so that
// the real NSTimer can retain this class. This reduces the above problem to just
// having to remember to invalidate the TargetOwnedTimer before releasing it in the
// real object's dealloc method (which is a lot easier to remember and verify).
// When the real object dealloc method is called, it can manually deallocate the
// TargetOwnedTimer using invalidate (removing the refcount) followed by release.
//
// Basically this just makes timer memory management work like it does for
// NSNotifications.
@implementation TargetOwnedTimer
// ---------------------------------------------------------------------------
+ (TargetOwnedTimer *) scheduledTimerWithTimeInterval: (NSTimeInterval) seconds
target: (id) target
selector: (SEL) selector
userInfo: (id) userInfo
repeats: (BOOL) repeats
{
TargetOwnedTimer *toTimer = [[TargetOwnedTimer alloc] initWithTimeInterval: seconds
target: target
selector: selector
userInfo: userInfo
repeats: repeats];
[[NSRunLoop currentRunLoop] addTimer: [toTimer timer] forMode: NSDefaultRunLoopMode];
return [toTimer autorelease];
}
// ---------------------------------------------------------------------------
+ (TargetOwnedTimer *) scheduledTimerWithFireDate: (NSDate *) fireDate
interval: (NSTimeInterval) seconds
target: (id) target
selector: (SEL) selector
userInfo: (id) userInfo
repeats: (BOOL) repeats
{
TargetOwnedTimer *toTimer = [[TargetOwnedTimer alloc] initWithFireDate: fireDate
interval: seconds
target: target
selector: selector
userInfo: userInfo
repeats: repeats];
[[NSRunLoop currentRunLoop] addTimer: [toTimer timer] forMode: NSDefaultRunLoopMode];
return [toTimer autorelease];
}
// ---------------------------------------------------------------------------
- (id) initWithTimeInterval: (NSTimeInterval) seconds
target: (id) target
selector: (SEL) selector
userInfo: (id) userInfo
repeats: (BOOL) repeats
{
if ((self = [super init])) {
timerTarget = target;
timerSelector = selector;
timerUserInfo = userInfo;
dprintf ("TargetOwnedTimer for
timer = [[NSTimer timerWithTimeInterval: seconds
target: self
selector: @selector (timer:)
userInfo: NULL
repeats: repeats] retain];
if (!timer) {
[self release];
return NULL;
}
}
return self;
}
// ---------------------------------------------------------------------------
- (id) initWithFireDate: (NSDate *) fireDate
interval: (NSTimeInterval) seconds
target: (id) target
selector: (SEL) selector
userInfo: (id) userInfo
repeats: (BOOL) repeats
{
if ((self = [super init])) {
timerTarget = target;
timerSelector = selector;
timerUserInfo = userInfo;
dprintf ("TargetOwnedTimer for
timer = [[NSTimer alloc] initWithFireDate: fireDate
interval: seconds
target: self
selector: @selector (timer:)
userInfo: NULL
repeats: repeats];
if (!timer) {
[self release];
return NULL;
}
}
return self;
}
// ---------------------------------------------------------------------------
- (void) dealloc
{
dprintf ("TargetOwnedTimer for [timer release];
[super dealloc];
}
// ---------------------------------------------------------------------------
- (NSTimer *) timer
{
return timer;
}
// ---------------------------------------------------------------------------
- (void) invalidate
{
// Call before releasing TargetOwnedTimer so its refcount goes to 0
[timer invalidate];
}
// ---------------------------------------------------------------------------
- (BOOL) isValid
{
return [timer isValid];
}
// ---------------------------------------------------------------------------
- (void) fire
{
[timer fire];
}
// ---------------------------------------------------------------------------
- (NSDate *) fireDate
{
return [timer fireDate];
}
// ---------------------------------------------------------------------------
- (void) setFireDate: (NSDate *) date
{
[timer setFireDate: date];
}
// ---------------------------------------------------------------------------
- (NSTimeInterval) timeInterval
{
return [timer timeInterval];
}
// ---------------------------------------------------------------------------
- (id) userInfo
{
return timerUserInfo;
}
// ---------------------------------------------------------------------------
- (void) timer: (NSTimer *) timer
{
[timerTarget performSelector: timerSelector withObject: self];
}
@end