[NSTimer scheduledTimerWithTimeInterval:0.033 target:self selector:#selector(gameLoop)
userInfo:nil repeats:YES];
I don't do anything to dealloc or kill this timer after starting it.
Is it safe? Or will it cause me to leak memory?
It shouldn't leak memory. You're not retaining the timer. The run loop will retain it (I think), but it'll release it when it no longer needs it.
The timer is retained by the run loop, so you don't need to retain it yourself.
However the timer will retain its target, so as long as it's repeating and you don't invalidate it, your target object won't be deallocated. You'll need to choose a good time to call invalidate on it which will cause the run loop to release it.
Note that you shouldn't also retain the timer yourself, at the risk of a retain cycle.
(I borked an answer to this very question yesterday and got schooled on it. Trying to atone.)
Related
If I create a timer which never repeats
[NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:#selector(updateSystems) userInfo:nil repeats:NO];
do I still need to invalidate it?
Also, is releasing the instance one step in the invalidate method? Since I'm not using alloc my timer variable shouldn't have to be released, although when creating a timer it automatically should create a new thread? Does the timer still add up on the stack?
I need some clarity.
Thank you
See the documentation for the method. It says:
repeats
If YES, the timer will repeatedly reschedule itself until invalidated. If NO, the timer will be invalidated after it fires.
You are not creating any pointer to NSTimer. You are using it directly through a class method. This method does not creates a new instance of NSTimer so you don't have to release it.
I am creating a repeating NSTimer object that calls the -Loop() method every iteration of the run loop:-
NSTimer *loopTimer = [NSTimer scheduledTimerWithTimeInterval:TIMER_INTERVAL target:self selector:#selector(Loop) userInfo:nil repeats:YES];
Within Loop() I am invalidating the timer when it becomes necessary:-
[loopTimer invalidate];
However, after using -invalidate(), the Touch handling methods -touchesBegan(), -touchesEnded() stop responding to touch events. Does NSTimer affect the run loop to somehow?
what else you are using in loop method...
I am not sure what problem exactly you have.Anyway try this...
You can use schedular method..
[self schedule:#selector(loop) interval:TIMER_INTERVAL];
If you unscheduling with in the same function,you can use
[self unschedule:_cmd];
otherwise,
You can use [self unschedule:#selector(loop)];
This sounds like you didn't correctly retain the view or have released it once too often. A NSTimer retains its target object, and once you invalidate a timer it releases its target object. In your case, that seems to make the retain counter drop to 0 and it thus gets deallocated.
You can verify this by adding a NSLog right in front of your [loopTimer invalidate]; and another one at the start of your view's dealloc. My bet is that you will see dealloc's log message immediately after your invalidate log.
No, it doesn't affect your runloop in anyway. Infact I checked it myself, after i invalidated the timer i was able to process touches on screen.So i guess problem is something else.
I seem to have fixed the problem by putting -invalidate() inside the -dealloc() method. So when i want end the loop i call [self.view removeFromSuperView] and this calls invalidate eventually.
Putting -invalidate() inside view lifecyle seems to deallocate the view...
I have a weird problem invalidating NSTimer. As long as the user is on a particular screen, I need to constantly update it. I'm using NSTimer to accomplish it. I wrote the below piece of code in viewDidLoad method.
- (void)viewDidLoad {
self.pollServerForUpdates = [NSTimer scheduledTimerWithTimeInterval:2.0
target:self
selector:#selector(fetchNewDataFromServer:)
userInfo:nil
repeats:YES];
}
Problem is when I try to invalidate the timer. As I want the app to stop polling the server when the user leaves the screen, I put the timer invalidation code in viewWillDisappear method.
-(void) viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:YES];
//NSLog(#"%d",[self.view retainCount]);
[self.pollServerForUpdates invalidate];
self.pollServerForUpdates = nil;
}
I use a navigation controller to go back and forth between my main view and the polling view. The app crashes if I move between my main view and polling view real fast. I enabled the NSZombie to see whats happening and this is what I get
*** -[CALayer retainCount]: message sent to deallocated instance 0x1c3be0
If I remove the timer invalidation my app works fine. But I want to stop the polling (timer) as soon as the user leaves the screen.
I believe this is happening because the timer is called a split second before the view is released, how do I avoid it? Do I need to change my design? Any help will be appreciated.
p.s: I can't use push notifications on this screen.
It may be that the bug is somewhere else, with some other class that is using your view without holding a reference to it. If you don't invalidate your timer than it will have a reference to your view forever, potentially extending its lifespan and masking memory management bugs elsewhere in your code.
Try breaking on exceptions, and see where the call to the zombie is coming from.
Okay so I know I asked a similar question a while ago, but this is different. I now have two timers that go off on the TouchDown event of their respective buttons. On the TouchUpInside event I have respective code that tells each timer to [pressTimer invalidate] and pressTimer = nil. The problem that happens now since I added the second timer is that when the selector is triggered (thus releasing the button and triggering the TouchUpInside event) the app crashes and spits out an Not recognized [NSCFTimer -invalidate] or something like that. The buttons work normally, until the timer triggers, and even then no crash until I let up my finger. I think what's going on is that the TouchUpInside event is trying to invalidate an invalid/triggered timer, because it works fine while the timer is still running. I wonder why this is happening since it never happened before I added the second timer. My temporary fix is to set the repeating:YES portion of the timer, which supports my theory that it can't invalidate an invalid/triggered timer. Any suggestions?
*Bonus: On a related note, how many crashes do I need to get from people before they show up in iTunes Connect?
For one thing, the pressTimer=nil serves no purpose; nil assignment only releases objects when using a setter, on a retained property, i.e. self.timer = nil;. (this actually does: [self setTimer:nil]!)
But even then, if you misquoted yourself and did use a setter, an NSTimer instance need not be released, it only needs to be invalidated. Your "kind of quoted" error should not happen.
As for you theory: you may read invalidate as release in this context - you can't do it too often...
The manual states that invalidate needs to be called from the same thread where it was installed, beyond that there's really no reason for anything to crash.
For a game I'm developing, I call an expensive method from one of the touch processing routines. In order to make it faster, I decided to use performSelectorInBackgroundThread, so instead of:
[gameModel processPendingNotifications];
I switched to:
[gameModel performSelectorInBackground:#selector(processPendingNotifications) withObject:nil];
The first problem I had, is that processPendingNotifications did not have a NSRunLoop, so I added it, like this:
- (void)processPendingNotifications {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[pendingNotificationsQueue makeObjectsPerformSelector:#selector(main)];
[pendingNotificationsQueue removeAllObjects];
[pool drain];
}
So far so good. My problem is that some of the methods that are called from this background thread create new NSTimer instances. These instances, end up not firing. I think this is because the secondary thread I have doesn't have a (or is ending its) NSRunLoop. I'm starting the new timers by using:
[NSTimer scheduledTimerWithTimeInterval:20.0 target:self selector:#selector(timerFired) userInfo:nil repeats:NO];
My questions are:
Am I on the right path of suspecting the problem has to do with the NSRunLoop ?
Is there a way I can start a NSTimer from a background thread and attach it to the main thread's NSRunLoop ?
Yes, you need a runloop to dispatch the timer events
NSTimers are implicitly attached to the runloop of the thread they are created on, so if you want it to be attached to the main thread's runloop create it on the main thread using performSelectorOnMainThread:withObject:waitUntilDone:. Of course that code will execute on the main thread.
If your question is "Can I have a timer run on the main thread's run loop that directly runs a selector on another thread?" The answer is no, but the selector it fires on the main thread could just performSelector:onThread:withObject:waitUntilDone:. Of course that requires the thread you are trying to perform the selector against have an operational runloop.
If you want the code the timer is triggerring running on a background thread then you really need to get the background thread's runloop going, and if you do that then you don't need to schedule anything with the main thread since you will have an operational runloop.
The NSTimers actually just periodically fire events into the enclosing NSRunLoop, which each thread has (or should have). So, if you have a child (or background) process running in a different thread, the NSTimers will fire against that thread's NSRunLoop instead of the application's main NSRunLoop.
You could ensure that timers are always created against the main runloop by sending it the addTimer:forMode: message with your newly instantiated (but not started) NSTimer. Accessing the main application's run loop is done with [NSRunLoop mainRunLoop], so regardless of which thread you're in, doing [[NSRunLoop mainRunLoop] addTimer:[NSTimer timerWithTimeInterval:20.0 target:self selector:#selector(timerFired) userInfo:nil repeats:NO]] forMode:NSDefaultRunLoopMode] will always schedule the timer against the main runloop.
However, bear in mind that the execution is not guaranteed at that interval, and if your main loop is busy doing something your timer will be left waiting until it's ready.
It's worth considering NSOperation and NSOperationQueue if you really want background activity to occur.