Passing Data through NSTimer UserInfo

2019-03-08 19:30发布

问题:

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];
}

回答1:

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.



回答2:

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];

}


回答3:

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
}


回答4:

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.



回答5:

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];

  }


回答6:

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