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.
精彩评论