开发者

Set Timer from another class to fire in another view

EDIT: Added the NSRunLoop from my code, as mentioned by Deepak below. This was originally in my code and forgot to add as it was commented out.

I have 2 classes: MainViewController, and ConfigViewController. The user switches to the ConfigView and uses a UIDatePicker / UIButton combo to set a Date/Time. Upon grabbing the correct time from the UIDatePicker object, I setup a NSTimer to fire as per the following code:

ConfigViewController.m

-(IBAction)setAlarmDate:(i开发者_如何学Pythond)sender {
//Instantiate to get access to doAlarm:
MainViewController *mvc = [[MainViewController alloc] init];

dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateStyle:NSDateFormatterMediumStyle];
[dateFormat setTimeStyle:NSDateFormatterShortStyle];
NSString *target = [NSString stringWithFormat:@"%@",[dateFormat stringFromDate:datePicker.date]];

alarmDate = [datePicker date];

mvc.fireTimer = [[NSTimer alloc] 
                  initWithFireDate:alarmDate interval:1 target:mvc
                  selector:@selector(doAlarm:) userInfo:nil repeats:YES];

NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addTimer:mvc.timer forMode:NSDefaultRunLoopMode];


[self dismissModalViewControllerAnimated:NO];
}

The doAlarm: method is as follows:

MainViewController.m

- (void)doAlarm:(NSTimer *)timer {

NSLog(@"Called doAlarm:");

UIImage *ac = [UIImage imageNamed:@"alarmclock.png"];
[self.alarmview setImage:ac];
[self.alarmview setHidden:NO];
[self.view addSubview:alarmview];
[self.view bringSubviewToFront:alarmview];

}

However, when I set the alarm date, the timer fails to fire. I think the following problems are afoot:

1) I am instantiating a new instance of the MainViewController class, setting the Timer going and then passing control back to the "original" instance of MainViewController when I dismiss the MVC. At this point, probably the "new" instance of MainViewController is nothing but a dangling pointer, and is never referenced again anyway, hence no segfault.

2) doAlarm: references self.view, which is supposed to be the MainViewController.view, but as it's instantiated in the scope of ConfigViewController, the alarm image would never be seen anyway...

I imagine my theories are a bit unfounded, but with my current level of knowledge, they make sense to me.

Any light you can shed on the above would be smashing.

Many thanks!

swisscheese.


If you go to the documentation for initWithFireDate:interval:target:selector:userinfo:repeats:, you will notice that while the timer is initialized it is not scheduled yet. You will need to add it a NSRunLoop to make it work. Do this after you have initialized an alarm as above.

[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];

You can also consider using the scheduledTimerWithTimeInterval:target:selector:userInfo:repeats: which does that for you.

There are of course few other things here.

New MainViewController object

You guessed it right. You are creating an unrelated object which will just leak. To solve this you can maintain a weak reference to the MainViewController object. You can do this as follows -

@class MainViewController; // Forward declaration 

@interface ConfigViewController {
    ...
    MainViewController *mainViewController;
}
...
@property (nonatomic, assign) MainViewController *mainViewController;
...
@end

in ConfigViewController.m:

#import "MainViewController.h"

@implementation ConfigViewController

@synthesize mainViewController;
...

- (IBAction)setAlarmDate:(id)sender {
    dateFormat = [[NSDateFormatter alloc] init];
    [dateFormat setDateStyle:NSDateFormatterMediumStyle];
    [dateFormat setTimeStyle:NSDateFormatterShortStyle];

    alarmDate = [datePicker date];

    mainViewController.fireTimer = [[NSTimer alloc] 
                  initWithFireDate:alarmDate interval:1 target:mainViewController
                  selector:@selector(doAlarm:) userInfo:nil repeats:YES];

    NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
    [runLoop addTimer:mainViewController.timer forMode:NSDefaultRunLoopMode];

    mainViewController = nil;
    [self dismissModalViewControllerAnimated:NO];
}
...
@end

Timer

I am not sure if you need a repeating timer here. There is an alternative - performSelector:withObject:afterDelay:. This will invoke a method on an object this is called after the desired delay. Does what it calls itself. It will be something like this -

[mvcReference performSelector:@selector(doAlarm) 
                   withObject:nil 
                   afterDelay:[datePicker.date timeIntervalSinceNow]];

doAlarm:

I did not get what you wanted to know here but you are right. self is a reference to the MainViewController object. Unless you retained the view controller that you displayed modally, it would have been dismissed and deallocated by then.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜