I would like to run NSTimer using BackGround thread for that I have written below code but my NSTimer method is not get calling!
can some one help me please
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
NSOperationQueue* operationQueue = [[NSOperationQueue alloc] init];
[operationQueue addOperationWithBlock:^{
// Perform long-running tasks without blocking main thread
[NSTimer scheduledTimerWithTimeInterval:2
target:self
selector:#selector(targetMethod)
userInfo:nil
repeats:YES];
}];
}
-(void)targetMethod{
NSLog(#"Timer Called");
}
You can use GCD dispatch queue for BackGround thread :=
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void)
{
NSTimer *timer = [NSTimer timerWithTimeInterval:0.5
target:self
selector:#selector(timerFired)
userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
dispatch_async(dispatch_get_main_queue(), ^(void)
{
});
});
Krish, you seem to be getting yourself on a wrong path here.
First, you should create timers while on the main thread by just calling scheduledTimerWithTimeInterval. Putting the call scheduledTimerWithTimerInterval onto an operationQueue is pointless. Timers are called from their thread's runloop. Trying to dispatch them with a different operationQueue will get you into trouble.
If you want the actual operation of a timer to happen in the background, then use dispatch_async (...) in your timer callback method to run code in the background.
Unless you have a legitimate reason to do otherwise, schedule your timer on the main thread.
Your callback method should have a parameter for the timer. That way your callback method can manipulate the timer, for example by invalidating it. scheduledTimer... also returns the timer. Usually you would store that timer object so that you can invalidate it when your view disappears. If you don't do that, you will get a new NSTimer every time viewDidLoad is called. That is you will have more and more timer callbacks running as your app is running.
Learn GCD (grand central dispatch). It is much, much simpler than operation queues. You shouldn't use operation queues unless you have a real good reason.
When you ask "how do we stop the background thread" - you don't. You dispatch code to it, the code runs, and as long as there is code dispatched, it will run. If there is no code dispatched the the background, it stops running until you dispatch code again. That's how it is supposed to work.
If you meant "how do I stop the timer" - that's what invalidate does.
PS. If you wanted to schedule a timer from a background thread (which you don't want, believe me), a correct answer can be found here: iOS5 ARC is it safe to schedule NSTimers from background selectors?
An NSTimer is not actually running on a thread. Scheduling a timer to the main thread (actually its NSRunLoop) will still allow it to handle events and perform other operations before the timer fires.
When the timer fires, the main thread NSRunLoop will invoke the target+selector and then continue waiting for the next event.
I would suggest you replace your code with:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[NSTimer scheduledTimerWithTimeInterval:2
target:self
selector:#selector(targetMethod)
userInfo:nil
repeats:YES];
}
-(void)targetMethod{
NSLog(#"Timer Called");
}
Note: If targetMethod needs to perform expensive operations, it is recommended that it schedule them to a background thread.
The reason your timer never fires, is that the background thread used by your NSOperationQueue probably doesn't have an NSRunloop. NSTimer instances schedule themselves on the NSRunloop of the thread on which they are created/initialized.
B.T.W if you ever need to stop (invalidate) an NSTimer - you must do this on the same thread where you created it.
The main thread has its NSRunloop up and running in advance, since all UI (and UI event handling) is done on that runloop. That's why it's easiest to use NSTimer on the main thread.
A possible solution, is to create an NSRunloop for the background thread you're going to use, e.g. by calling:
[NSRunloop currentRunLoop];
within the block-operation you add to the NSOperationQueue. Documentation says that if there is no runloop for the current thread, currentRunLoop will create one and return it when this is called.
Now another possible issue is, that NSOperationQueue (especially the way you created and initialized it) makes use of undetermined number of threads, can create and dispose threads along the way, and in any way doesn't commit to their lifetime. So... my solution may break.
Instead of using NSOperationQueue, you should create a background thread, create and start its NSRunloop, and then schedule your timer on that runloop.
Another possible solution was already mentioned here - using dispatch_source timer instead of NSTimer, and then - there are better answers than this already.
Related
I have an NSTimer that should be running all the time the app is active. It is intended to show a countdown that depends on certain user's actions. I fire this timer this way:
self.timer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(notifyTimerTick:)
userInfo:nil
repeats:YES];
Then, in the method notifyTimerTick:, I update the countdown label I show to users and, if the countdown is over, I invalidate the timer, I look for a new countdown, and I fire the timer again.
I'm not having troubles being the UI blocked doing this way, but on the other hand, I've found that sometimes the notifyTimerTick: selector call is significantly delayed: I have a view that takes a couple of seconds to be completely loaded, and I've seen that timer's selector is not called until the corresponding view controller's viewDidLoad delegate method is called.
I've read several posts dealing with timers blocking the UI, but I'm not sure how to deal with a timer getting blocked by the UI... what the best way to handle this should be?
Thanks in advance
You need to use a different run loop mode.
When you use the ScheduledtimerWithTimeInterval class method, the timer is scheduled on the current run loop.
Instead do something like this:
NSTimer *labelTick = [NSTimer timerWithTimeInterval:1 target:self selector:#selector(updateTime:) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:labelTick forMode:NSRunLoopCommonModes];
I have created a view in which background is changing randomly.A total of 10 images .that i am using for displaying .
Below is the code for Background images.
- (void)viewWillAppear:(BOOL)animated
{
myTimer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:#selector(changeImage) userInfo:nil repeats:YES];
}
-(void)changeImage
{
self.imgView.image=nil;
int randNum = rand() % (9 - 1) + 1;.
NSString *num = [NSString stringWithFormat:#"%d", randNum];
self.imgView.image=[UIImage imageNamed:[NSString stringWithFormat:#"%#.png",num]];
}
Code is working fine .
My question is;its showing over 34mb memory usage ,when i push other view it's still over 34 mb.although i have make the myTimer variable nil;
- (void)viewWillDisappear:(BOOL)animated
{
[myTimer invalidate];
self.imgView.image=nil;
myTimer = Nil;
}
How can i manage memory usage here?
You have to invalidate your timer, setting it to nil will not prevent it from firing:
Once scheduled on a run loop, the timer fires at the specified interval until it is invalidated. A non-repeating timer invalidates itself immediately after it fires. However, for a repeating timer, you must invalidate the timer object yourself by calling its invalidate method. Calling this method requests the removal of the timer from the current run loop; as a result, you should always call the invalidate method from the same thread on which the timer was installed. Invalidating the timer immediately disables it so that it no longer affects the run loop. The run loop then removes the timer (and the strong reference it had to the timer), either just before the invalidate method returns or at some later point. Once invalidated, timer objects cannot be reused.
So you need
[myTimer invalidate];
myTimer = nil;
To stop it running.
In terms of the memory use; what are you expecting to happen? If you've just pushed a new view on the navigation stack the old view is still around and its contents will be in memory. The layer cache won't be removed until the app comes under memory pressure, and 34MB probably isn't going to cause that.
As pointed out in comments, imageNamed caches images in memory (again, until you come under memory pressure) and you also need to call the super implementations of viewXXappear to make sure UIKit is handling things properly.
Try simulating a memory warning and seeing what happens. I don't think you've got anything to worry about, now you're invalidating the timer.
Also try adding to viewdiddisappear,
self.imgView=nil;
The method imageNamed: caches the image it only evict it during memory pressure situations, try to use imageWithContentsOfFile: and when you want to force a release set the image property of the image view to nil.
I have a NSTimer defined like:
[NSTimer scheduledTimerWithTimeInterval:self.speed target:self selector:#selector(displayTextOverTime:) userInfo:nil repeats:YES];
The method displayTextOverTime displays some text, then once the last piece of text is displayed it invalidates it's timer and calls another method for processing (which itself may need to start a NSTimer to display text).
Is there a standard way to do this sort of thing?
My original approach was to simply have displayTextOverTime call the other method directly, but that's getting me some weird behavior where I have nested NSTimers (which never die, because even though they are invalidated and won't loop again, they aren't 'done' because the timer inside of them is still running...or...something?), which is NOT what I want. Is there some way to say "call this method, but do it outside of this NSTimer", like back on the main thread?
Specifically, I am seeing more timers being killed than started (I have a method for killing any passed in timer so I can log when it's being killed).
For an exact answer to your question, you could use Grand Central Dispatch to run a function outside of your timer. Were you want to run the function put this:
dispatch_async(dispatch_get_main_queue(), ^{
(your function or block of code goes here)
});
That will run the block outside of your timer. You can chose to do this on the main thread, global thread, or custom thread with a queue that you create.
I wrote an application that use MKMapView. This application use a timer to update some information on screen. Actually, when user touch the map and start the drag, the timer isn't fired anymore until the user release the touch. I notice that with the new iOS 6, this problem disappears. However I need to support also iOS 5. I haven't figure out if only timers aren't fired or if no events are processed at all. Any idea?
Ok I found the solution here: UIScrollView pauses NSTimer until scrolling finishes
Basically you have to put the NSTimer in it's own run loop.
Hmm, that would suggest that the timers and the touch processing code are being handled by the same runloop, or possibly that the touches are blocking so when the timer completion code tries to run, it can't. Try using asynchronous blocks with completion handlers to run your timers.
- (void)startTimerInBackground {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
//Start timer here, set completion method to be called
NSTimer *t = [NSTimer scheduledTimerWithTimeInterval: 2.0
target: self
selector:#selector(timerCompletionMethod:)
userInfo: nil repeats:NO];
});
}
- (void)timerCompletionMethod:(NSTimer *)timer {
//Switch back to main thread here for completion code
dispatch_async(dispatch_get_main_queue(), ^(void) {
});
}
See if that helps, do note though that timers are not reliable, and if you need very accurate timing you should probably look at alternatives, there is some very good info here:
How do I use NSTimer?
I don't understand it at all but NSTimer in my app definitely is running in background. I have a NSLog in method run by the timer and it is logging while it's in background. It's on iPhone 4 with iOS 4.2.1. I have declared location background support in Info.plist.
I read the docs and many discussions here and elsewhere and it shouldn't be possible. Is it an iOS bug? Or undocumented feature? I don't want to use it and find out in near future, for example with coming of iOS 4.3 that Apple silently "fixed" it and the app won't be working.
Does anybody know more about it?
NSTimer is going to fire whenever the main runloop is running. Apple makes no promises that I know of to unschedule timers or to prevent the main runloop from running. It's your responsibility to unschedule your timers and release resources when you move to the background. Apple isn't going to do it for you. They may, however, kill you for running when you are not supposed to or using too many seconds.
There are many holes in the system that will allow an app to run when it isn't authorized to. It would be very expensive for the OS to prevent this. But you cannot rely on it.
You can have a timer fire while in background execution mode. There are a couple of tricks:
You need to opt into background execution with beginBackgroundTaskWithExpirationHandler.
If you create the NSTimer on a background thread, you need to add it to the mainRunLoop manually.
- (void)viewDidLoad
{
// Avoid a retain cycle
__weak ViewController * weakSelf = self;
// Declare the start of a background task
// If you do not do this then the mainRunLoop will stop
// firing when the application enters the background
self.backgroundTaskIdentifier =
[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:self.backgroundIdentifier];
}];
// Make sure you end the background task when you no longer need background execution:
// [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskIdentifier];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Since we are not on the main run loop this will NOT work:
[NSTimer scheduledTimerWithTimeInterval:0.5
target:self
selector:#selector(timerDidFire:)
userInfo:nil
repeats:YES];
// This is because the |scheduledTimerWithTimeInterval| uses
// [NSRunLoop currentRunLoop] which will return a new background run loop
// which will not be currently running.
// Instead do this:
NSTimer * timer =
[NSTimer timerWithTimeInterval:0.5
target:weakSelf
selector:#selector(timerDidFire:)
userInfo:nil
repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer
forMode:NSDefaultRunLoopMode];
// or use |NSRunLoopCommonModes| if you want the timer to fire while scrolling
});
}
- (void) timerDidFire:(NSTimer *)timer
{
// This method might be called when the application is in the background.
// Ensure you do not do anything that will trigger the GPU (e.g. animations)
// See: http://developer.apple.com/library/ios/DOCUMENTATION/iPhone/Conceptual/iPhoneOSProgrammingGuide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html#//apple_ref/doc/uid/TP40007072-CH4-SW47
NSLog(#"Timer did fire");
}
Notes
Apps only get ~ 10 mins of background execution - after this the timer will stop firing.
As of iOS 7 when the device is locked it will suspend the foreground app almost instantly. The timer will not fire after an iOS 7 app is locked.