I am trying to pass data through userInfo for an NSTimer call. What is the best way to do this? I am trying to use an NSDictionary, this is simple enough when I have Objective-C objects, but what about other data? I want to do something like this, which doesn't work as is:
- (void)play:(SystemSoundID)sound target:(id)target callbackSelector:(SEL)selector
{
NSLog(@"pause ipod");
[iPodController pause];
theSound = sound;
NSMutableDictionary *cb = [[NSMutableDictionary alloc] init];
[cb setObject:(id)&sound forKey:@"sound"];
[cb setObject:target forKey:@"target"];
[cb setObject:(id)&selector forKey:@"selector"];
[NSTimer scheduledTimerWithTimeInterval:0
target:self
selector:@selector(notifyPause1:)
userInfo:(id)cb
repeats:NO];
}
You have to wrap the information correctly into the dictionary:
- (void) play:(SystemSoundID)sound target:(id)target callbackSelector:(SEL)selector
{
NSLog(@"pause ipod");
[iPodController pause];
theSound = sound;
NSMutableDictionary *cb = [[NSMutableDictionary alloc] init];
[cb setObject:[NSNumber numberWithInt:sound] forKey:@"sound"];
[cb setObject:target forKey:@"target"];
[cb setObject:NSStringFromSelector(selector) forKey:@"selector"];
[NSTimer scheduledTimerWithTimeInterval:0
target:self
selector:@selector(notifyPause1:)
userInfo:cb
repeats:NO];
[cb release];
}
In notifyPause1:
, you retrieve everything:
- (void)notifyPause1:(NSTimer *)timer {
NSDictionary *dict = [timer userInfo];
SystemSoundID sound = [[dict objectForKey:@"sound"] intValue];
id target = [dict objectForKey:@"target"];
SEL selector = NSSelectorFromString([dict objectForKey:@"selector"]);
// Do whatever...
}
As the timer is a repeating timer, you do not need the dictionary anymore, so you can release it.
Your call is right, but you don't have to cast the dictionary to id. You can get the userInfo back with the following line in your notifyPause1: method:
- (void)notifyPause1:(NSTimer *)timer {
NSDictionary *dict = [timer userInfo];
}
You may ask for the timer in the method given to the selector,
Then you can grab useInfo from that timer (timer.userInfo):
- (void)settingTimer
{
[NSTimer scheduledTimerWithTimeInterval:kYourTimeInterval
target:self
selector:@selector(timerFired:)
userInfo:yourObject
repeats:NO];
}
- (void)timerFired:(NSTimer*)theTimer
{
id yourObject = theTimer.userInfo;
//your code here
}
sound
and selector
aren't Objective-C objects: sound
is an unsigned number and selector
is a pointer to a C struct . That's likely to cause a crash of some sort.
You'll want to use NSValue to hold the value for selector
and NSNumber to hold the value for sound
. NSValue and NSNumber are objects and will work with the NSMutableDictionary.
Laurent Etiemble's method works well for making good use of timer methods in subclasses of UIViewcontroller which can be easily used in a number of different view controllers.
Below is a way to write a generic score display which can be used over and over in the same app by passing the view through NSTimer userInfo.
.h (subclass of UIViewcontroller)
- (NSTimeInterval) timeSet;
- (void) timeRun: (UISegmentedControl*)scoreDisplay;
- (void) timeWrite: (NSTimer *)timer;
.m
- (NSTimeInterval) timeSet {
return [self.startTime timeIntervalSinceNow];
}
- (void) timeWrite: (NSTimer *)timer
{
NSDictionary *dict = [timer userInfo];
UISegmentedControl *scoreDisplay = [dict objectForKey:@"scoreDisplay"];
int myint = round([self timeSet]);
NSString* buttonWrite = [[[NSString stringWithFormat:@"Score: %d", self.answered] stringByAppendingString:[NSString stringWithFormat:@" Errors: %d", self.errors]] stringByAppendingString:[NSString stringWithFormat:@" Time: %d", (myint*-1)]];
[scoreDisplay setTitle:buttonWrite forSegmentAtIndex:0];
}
- (void) timeRun: (UISegmentedControl*)scoreDisplay
{
self.startTime = [NSDate date];
NSMutableDictionary *cb = [[NSMutableDictionary alloc] init];
[cb setObject:scoreDisplay forKey:@"scoreDisplay"];
self.myTimer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:@selector(timeWrite:)
userInfo:cb
repeats:YES];
}
In the view controller
.m
- (void)viewDidLoad
{
[super viewDidLoad];
//load ScoreDisplay
[self timeRun:self.scoreDisplay];
}
You should not cast your objects to id when assigning to the dictionary. Any object that can be embedded directly into a NSDictionary
already derives from NSObject
and are implicitly concidered cast to id.
Store the name of the selector as an NSString
(using NSStringFromSelector()
) and then convert it back to a selector using NSSelectorFromString()
Claus