Sometimes it’s necessary to perform an action some time in the future, whether it’s disabling a button for a certain time interval after it’s pressed, performing an animation after a short wait, or triggering a reload of some data.
NSTimer is great for that purpose, as well as repeatedly performing actions, but it’s most convenient utility method, scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:
only takes one ‘id
‘ argument. Using the NSInvocation equivalent, scheduledTimerWithTimeInterval:invocation:repeats:
requires creating and setting up the NSInvocation itself, which is always verbose and a pain.
NSProxy is a wonderful little construct that lets us interact with it like we were talking to the original object. I first learned how it works from a post by Shaun Wexler which shows an easier way to send a message on the main thread, by using the NSInvocation given via the NSProxy’s forwardInvocation:
method. The same technique can be used to easily create a configured NSTimer.
So, instead of some awful thing like this:
NSInvocation *i = [NSInvocation invocationWithMethodSignature:[mapView methodSignatureForSelector:@selector(selectAnnotation:animated:)]]; [i setTarget:mapView]; MKAnnotation *annotation = view.annotation; [i setArgument:&annotation atIndex:3]; BOOL flag=YES; [i setArgument:&flag atIndex:4]; [NSTimer scheduledTimerWithTimeInterval:0.5 invocation:i repeats:NO]; |
We can do this:
[(MKMapView*)[TPTimerProxy scheduledTimerProxyWithTarget:mapView timeInterval:0.5 repeats:NO] selectAnnotation:view.annotation animated:YES]; |
Here’s the juice:
@interface TPTimerProxy : NSProxy { id target; NSTimeInterval timeInterval; BOOL repeats; NSTimer *timer; } + (TPTimerProxy*)scheduledTimerProxyWithTarget:(id)target timeInterval:(NSTimeInterval)timeInterval repeats:(BOOL)repeats; - (void)invalidate; @end @implementation TPTimerProxy - (id)initWithTarget:(id)aTarget timeInterval:(NSTimeInterval)aTimeInterval repeats:(BOOL)repeatFlag { target = aTarget; timeInterval = aTimeInterval; repeats = repeatFlag; return self; } + (TPTimerProxy*)scheduledTimerProxyWithTarget:(id)target timeInterval:(NSTimeInterval)timeInterval repeats:(BOOL)repeats { return [[[TPTimerProxy alloc] initWithTarget:target timeInterval:timeInterval repeats:repeats] autorelease]; } - (void)invalidate { if ( timer ) { [timer invalidate]; timer = nil; } } - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector { return [target methodSignatureForSelector:selector]; } - (void)forwardInvocation:(NSInvocation *)invocation { [invocation setTarget:target]; timer = [NSTimer scheduledTimerWithTimeInterval:timeInterval invocation:invocation repeats:repeats]; } @end |
Would it be possible to release this code as a package and include a license (even if it’s “public domain license”)? It seems like a useful snippet, but at work we’re can’t use any unlicensed code. Thanks!