i am using the following method in a uiview subclass:
[self performSelector:#selector(timeout) withObject:nil afterDelay:20];
The method is called after 20 seconds as expected.
In another method i try to cancel the perform request using the following code:
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(timeout) object:nil];
i've also tried
[NSRunLoop cancelPreviousPerformRequestsWithTarget:self selector:#selector(timeout) object:nil];
both messages don't bring the expected result an the timeout method is still called.
can anybody explain me what i am doing wrong and how to do it the right way ?
cheers from austria
martin
Two points
1. Are both self same object??
2. Is [NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(timeout) object:nil]; performed on same thread on which you called [self performSelector:#selector(timeout) withObject:nil afterDelay:20]; ?
Check these two problems.
Use an NSTimer stored as an instance variable in your class. When you want to cancel the perform, invalidate and destroy the timer.
In your #interface:
#property (readwrite, retain) NSTimer *myTimer;
In your #implementation:
self.myTimer = [NSTimer scheduledTimerWithTimeInterval:20 target:self selector:#selector(timeout) userInfo:nil repeats:NO];
Then, if some condition happens and the timeout method should no longer be called:
[self.myTimer invalidate];
self.myTimer = nil; // this releases the retained property implicitly
Try this:
[self performSelectorOnMainThread:#selector(timeout) withObject:self waitUntilDone:NO];
You can do that with 2 ways :
You could use this which would remove all queued
[NSObject cancelPreviousPerformRequestsWithTarget:self];
you can remove each one individually
[NSObject cancelPreviousPerformRequestsWithTarget:self
selector:#selector(timeout)
object:nil];
Related
I need some UIViewControllers that receive a NSNotification from the app delegate. It's like a timer, but every UIViewController handle your way. My problem is: when I interact with user interface, the UIViewController doesn't receive the notification, causing problems.
Here is my code in AppDelegate:
-(void)updateCounter:(NSTimer *)theTimer{
[[NSNotificationCenter defaultCenter] postNotificationName:TimeTickNotification object:nil];
}
//*called by some trigger in the app
-(void) startTimer{
timer = [NSTimer
scheduledTimerWithTimeInterval:0.5
target:self
selector:#selector(updateCounter:)
userInfo:nil
repeats:YES];
}
I am handling the notifications in each UIViewController like this:
-(void) updateGlobalTime:(NSNotification *) notification{
totalTime = [NSNumber numberWithFloat:([ficha.tempoTotal floatValue] + STEP)];
}
-(void) viewWillAppear:(BOOL)animated {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(updateGlobalTime:)
name:TimeTickNotification
object:nil];
}
What should I do to interact with UI and update it at same time? Maybe the NSNotification is not being thrown while user interacts with UI.
You need to make sure you're updating any UI on the main thread. If you want to update the UI to have the new totalTime in a label or something, make sure the setText: function is running on the main thread. You can accomplish that with GCD, like this:
-(void) updateGlobalTime:(NSNotification *) notification{
totalTime = [NSNumber numberWithFloat:([ficha.tempoTotal floatValue] + STEP)];
// Update label on main thread
dispatch_async(dispatch_get_main_queue(), ^{
[label setText:totalTime];
});
}
The solution was to use NSRunLoop, as following:
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
timer = [NSTimer
scheduledTimerWithTimeInterval:0.5
target:self
selector:#selector(updateCounter:)
userInfo:nil
repeats:YES];
[runloop addTimer:timer forMode:NSRunLoopCommonModes];
[runloop addTimer:timer forMode:UITrackingRunLoopMode];
I one of my ViewController I want to call a method updateVisitotsLists on some time criteria's and am not able to decide which way will be best to achieve it.
1) Every time the view is loaded/appeared I want to call this method.
For this in viewDidAppear method I can call this before calling [super viewDidAppear];, so that works, I believe.
2) If user is on this view only, then I want to call this method after every 5 secs.
For this, I need to set a NSTimer. I want to stop this timer when viewDidDisappear - as don't want it running unnecessary. Should I use an UnScheduled Timer as shown here and start and stop in appear & disappear methods ? In viewDidAppear, initially I call this method, and then also set
NSTimer *t = [NSTimer scheduledTimerWithTimeInterval: 5.0
target: self
selector:#selector(updateVisitotsLists:)
userInfo: nil repeats:NO];
What will be the best way and methodology to achieve what I am looking for ? Any help is highly appreciated.
Thanks.
UPDATED :-
#Lord Zolt, as per your comment I did the following :-
//In .h
#property (strong, nonatomic) NSTimer *timer;
// .m
#synthesize timer;
- (void)viewDidLoad
{
........
[super viewDidLoad];
// CREATE TIMER
timer = [NSTimer scheduledTimerWithTimeInterval:5 target:self selector:#selector(onTimerCall:) userInfo:nil repeats:YES];
}
-(void) viewWillDisappear:(BOOL)animated {
[timer invalidate];
timer = nil;
[super viewWillDisappear:animated];
}
-(void) onTimerCall: (NSTimer *) _timer {
// UPDATE VISITOR'S LIST
[self updateVisitotsLists];
}
Is this proper ?
I would recommend using timers.
Create an NSTimer property, but I would recommend calling invalidate on them on viewWillDisappear.
If you don't call invalidate, when the view controller is dismissed or popped, it won't be deallocated, since NSTimer will keep it alive.
The code you posted is fine with a few modifications:
You don't need to #synthesize properties anymore (unless you overwrite both the setter and getter).
Don't set timer to nil.
Edit: If you want the timer to be related to the screen (aka it should be executed only when the screen is visible), you should initialise in viewDid(Will)Appear and stop it in viewDid(Will)Disappear.
[self performSelectorInBackground:#selector(method) withObject:nil];
-(void)method
{
timer1 = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(getLastImageName1) userInfo:nil repeats:YES];
runLoop = [NSRunLoop currentRunLoop];
[runLoop addTimer:timer1 forMode:NSRunLoopCommonModes];
[runLoop run];
}
-(void)viewdidunload
{
[timer1 invalidate];
timer1=nil;
}
I start Timer in HomeViewController even I invalidate, it keeps running in OtherViewController. What is wrong with my code?
First of all, when you're overriding life cycle methods, you should include a call to the super version of that method.
Second of all, Objective-C is case sensitive, so even if your app would try to call the life-cycle even, viewDidUnload, your method would simply never be called because that's what you titled your method.
Third of all, viewDidUnload was deprecated in iOS 6.0 and shouldn't be used at all by this point unless you're going way out of your way to support backward compatibility. It will never be called in iOS 6.0 and greater.
If you want the timer to stop when the user navigates away from the current view, you'll want something like this:
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
if (timer1.isValid) {
[timer1 invalidate];
}
timer1 = nil;
}
If you're looking for something else, you'll need to elaborate on what it is you want to accomplish exactly.
If you ARE working on a pre-iOS 6.0 project, for whatever reason, the reason your method isn't being called is at least in part because it is spelled wrong. Again, Objective-C is case sensitive. Your method name should be spelled viewDidUnload.
For future reference, the question shouldn't really be "why isn't my timer invalidating?" You should have start by using breakpoints or NSLog statements to determine whether or not your method, viewdidunload, which tries to invalidate the timer even fires. When you find out it's not being called, do a search to ask "How come viewdidunload isn't called?" Then you'll go fix the capitalization problem and the problem will (probably) remain, so do some more research. And if at the end, you still can't figure it out, as a worst case scenario, the post question should be "how come viewdidunload isn't called?"
timer1 = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(getLastImageName1:) userInfo:nil repeats:YES];
set colon for function in selector
-(void) getLastImageName1 :(NSTimer*)timer1
{
//Do your all process and invalidate after completion
[timer1 invalidate];
}
or if you want to remove timer after moving to next view controller use how #nhgrif mentioned
- (void)viewDidDisappear:(BOOL)animated
{
[timer1 invalidate];
}
[self performSelector:#selector(method) withObject:nil afterDelay:0.0];
-(void)method
{
timer1 = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(getLastImageName1) userInfo:nil repeats:YES];
runLoop = [NSRunLoop currentRunLoop];
[runLoop addTimer:timer1 forMode:NSRunLoopCommonModes];
[runLoop run];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
[timer1 invalidate];
timer1=nil;
}
There is no need to add the timer (again) on the main run loop. Or is it necessary for You to run it also in commonModes? For me it was never necessary.
From the NSTimer Documentation:
scheduledTimerWithTimeInterval:invocation:repeats:
Creates and returns a new NSTimer object and schedules it on the
current run loop in the default mode.
Since the NSRunLoop Documentation points out that you can add timer on several run loop modes, maybe this is the problem.
addTimer:forMode:
Discussion You can add a timer to multiple input modes. While running
in the designated mode, the receiver causes the timer to fire on or
after its scheduled fire date. Upon firing, the timer invokes its
associated handler routine, which is a selector on a designated
object.
Also I don't get why you are invoking the timer creation with performSelector?
I just wrote a minimalistic sample. thats totally working!
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(doWork:) userInfo:Nil repeats:YES];
}
- (void)viewDidDisappear:(BOOL)animated{
[self.timer invalidate];
[super viewDidDisappear:animated];
}
- (void) doWork:(id) userInfo
{
NSLog(#"Working again");
}
Hope this helps.
-(void)viewDidUnload is a delegate which fires on memory warning only and is deprecated after iOS 6. It will also never fire on the simulator.
Stop timer in
- (void)viewWillDisappear:(BOOL)animated
or in
- (void)viewDidDisappear:(BOOL)animated
I need to create a routine that save automatically a file content in a constant time period, ie, a backgroung loop that perform the save instructions. I thinked in use a recursive call of performSelector like below:
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[self performSelector:#selector(saveMethod) withObject:nil afterDelay:kTimeConstant];
}
- (void)saveMethod{
//The save logic should to be here
[self performSelector:#selector(saveMethod) withObject:nil afterDelay:kTimeConstant];
}
It works, but when I get out of viewController, it still running, and it must to stop.
are there any better way to execute it? Thank you!
This is probably a better implementation:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// Start timer and sets it to a property called saveTimer
self.saveTimer = [NSTimer scheduledTimerWithTimeInterval:2.0
target:self
selector:#selector(saveMethod:)
userInfo:nil
repeats:YES];
}
- (void)saveMethod:(NSTimer*)theTimer {
// The save logic should to be here
// No recursion
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
// Stop timer
[self.saveTimer invalidate];
}
This is running on the main thread so it is probably not the best implementation but it should work better than what you currently have.
There is a function NSRunLoop cancelPreviousPerformRequestsWithTarget:selector:object: which allows you to cancel the performSelector call. Call this when you unload the view controller
ie.
[NSRunLoop cancelPreviousPerformRequestsWithTarget:self selector:#selector(saveMethod) object:nil];
I want to update an UISlider according to a notification generated by another singleton class.
sondDuration=audioPlayer.currentItem.asset.duration;
songDurationinSeconds=CMTimeGetSeconds(sondDuration);
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(updateTime:) userInfo:nil repeats:YES];
This is my notofication generation.
According to this how I can update my UISlider in another ViewController Please help me.
You can use Delegate Method For that
Like the given Below:
http://www.roostersoftstudios.com/2011/04/12/simple-delegate-tutorial-for-ios-development/
I think you have to add a NSNotification in your viewcontroller which updates the UISlider
In your viewcontroller
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(receiveSliderUpdate:)
name:#"UpdateSlider"
object:nil];
- (void) receiveSliderUpdate:(NSNotification *) notification
{
// [notification name] should always be #"UpdateSlider"
// unless you use this method for observation of other notifications
// as well.
if ([[notification name] isEqualToString:#"UpdateSlider"])
// do something with your slider
}
In your controller add the code to notify your view controller
[[NSNotificationCenter defaultCenter]
postNotificationName:#"UpdateSlider"
object:self];