Having issues with iPhone NSTimer invalidation - iphone

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.

Related

Fire method when a specific nsdate has passed

I want my App to call a method at an setter NSDate (It only needs to happen when the app is active, I'll handle passed events on launch in the app delegate).
I read about some ways to achieve such a behavior: On the one hand to work with performSelector:withObject:afterDelay: (But this doesn't look like a good way for me) or on the other hand to work with an NSTimer.
What is the best way regarding the app performance? One requirement is that you can cancel the event.
Using NSTimer is a simple solution. It has a property called fireDate that tells you when it's going to fire next. And it's a writeable property, so you can set that date to whatever you want.
Cancel the event by invalidating the timer object.

Objective-C multithreading, what happens when an object gets dealloc while its methods are being executed? (And how to prevent it?)

I am writing an app for the iPhone and arrived at this situation.
I have a view controller, myViewController, that will dealloc whenever the user taps the "back" button on the screen. There a thread in the background that communicates with a remote server and may message a method, updateUI, method in myViewController.
What would happen if the background thread messages updateUI in myViewController, but the user just happened to tapped the "back" button at the right time such that it causes myViewController to dealloc while updateUI is still executing?
My guess is that the dealloc method will run and the app might crash if updateUI ends up using a null pointer. Assuming this is the case, the current solution I have is:
[self retain];
// updateUI code here
[self release];
I am unsure if this is the best solution, as I feel that this is a common problem when dealing with multiple threads.
Is my assumption correct? If so, is there a better solution?
What you are describing is known as a "race condition." Race conditions can be difficult to identify in testing, track down once reported, and reproduce because sometimes execution in the debugger can effectively modify how the code is being executed (avoiding the condition that one is trying to reproduce). Race conditions are one of the major pitfalls in concurrent programming - making the area deceptively difficult to do well.
In principle, it is a best practice to minimize the use of shared resources and closely qualify how the sharing is coordinated when implementing concurrency. If an object is shared across multiple threads, it should be retained by each of them to ensure that the object stays in-scope while each thread completes its processing.
Apple has been taking steps to simplify implementing concurrency. This is a good starting point for familiarizing yourself with the topic on iOS.
http://developer.apple.com/library/ios/#documentation/General/Conceptual/ConcurrencyProgrammingGuide/Introduction/Introduction.html%23//apple_ref/doc/uid/TP40008091
It's also useful to be aware that Objective-C 2.0's properties can support atomic operations (and are atomic by default, thus the nonatomic keyword to disable this default).
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Articles/ocProperties.html
And, this is the old-school guide to threads (out of favor approach, but still useful background - be sure to be familiar with NSLock).
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Multithreading/Introduction/Introduction.html%23//apple_ref/doc/uid/10000057i
Whenever some part of your code depends on another, it should retain this dependency until it is not needed. In your case the background worker should retain the controller and only release it when the work is done (or cancelled).
If you dealloc and then nil your objects then this shouldn't be an issue - you can send messages to nil in objective-c.
Alternatively, if you wanted the viewController to get the message and if your targeting iOs 4, you can use blocks and GCD. Blocks auto retain objects and therefore if a block references your viewController, would keep it around for as long as its needed, even if -(void)dealloc ; has been called.
Here is a decent Block tutorial
Yes, your app will crash, likely with something along the lines of EXC_BAD_ACCESS.
As far as multithreading, you will want to retain your objects until everything is done with them and program defensively. Check for the existence of objects before trying to manipulate them.

Invalidating an NSTimer?

If I have an NSTimer that starts in viewdidload, where is the proper place to invalidate it when leaving that view? Is it also necessary to release it as well?
If you create a timer with NSTimer scheduledTimerWithTimeInterval... then you don't need to release it, as you don't own it.
Now important thing is that the timer class retains the target, and in most cases we use self as the target. If the timer is not repetitive , then after the timer handler is completed, it automatically becomes invalid and the target is released. So you have nothing to do. But if your timer is still pending when leaving the view (this happens if you leave before the timer is fired or the timer is repetitive) then you need to invalidate it. The place MUST NOT be the dealloc method. As the timer itself retains the target, dealloc won't be called until the timer is invalid.
So it's better to invalidate when you know that you no longer need this. This might be the action which moves to the other view. Say user taps a button and in the button handler you move to other view. You can invalidate in this button handler.

How to solve this performSelector:withObject:afterDelay: problem?

I have a view controller, which calls performSelector:withObject:afterDelay. However, if I remove that view controller right after calling this, my app crashes as soon as the system tries to perform the delayed selector on that (deleted) view controller.
Now how can I go about this? I need to get rid of the view controller to save memory, so there's no way to let it hang around.
Any way to cancel a delayed perform selector before it performs?
I suggest to use an NSTimer instead. You can simply invalidate the timer to make sure it will never be called after the UIViewController has gone away. A good moment to invalidate the timer is for example in viewWillDisappear:.
This does mean that the timer is owned by the view controller. But that is a good design anyway.
You can't perform a selector on a deleted object, you either need to have the object around, or do the work with some other smaller object that you can have hanging around.
To cancel there is a cancelPreviousPerformRequestsWithTarget:selector:object: or cancelPreviousPerformRequestsWithTarget: method.

Timer and animation events trumping TouchesEnded events

I've implemented a tap-and-hold handler using an NSTimer that I first set in the TouchesBegan overload.
However, what I actually want is for an action to be continuously performed in quick-fire succession while the touch is being held. So, on timer expiry I call a handler to do the work, which then sets another timer and the cycle continues until the TouchesEnded comes in and cancels it, or another terminating condition is met.
This works fine, until my handler code triggers an animation to go off at the same time.
Now we have animation events and timer events going off, and in all that we need to handle TouchesEnded as well.
What I am finding is that, if the animation is triggered, and I set my timer to less than 0.025 seconds, my TouchesEnded event doesn't come through until the timer cycle stops (the other terminating condition). Setting a slower timer, or not triggering the animation, make it work (TouchedEnded comes in straight away), but are not what I want.
Obviously this is all on the device (release build - no NSLogs) - in the sim it all works fine
Is there any way of setting the relative priorty of these events - or is it likely I'm missing something else obvious here?
[Update]
I've worked around this in this instance by doing the continuous part without visual feedback until it's done (which from this users perspective is instant). I think this is ok for now. I'd still like to hear any more thoughts on this (Jeffrey's idea was good), but I'm not waiting on tenterhooks now.
Try writing your own Timer-type class by spawning off onto a thread. Example:
BOOL continue = YES; //outside of your #implementation
-(void)doLoop
{
while(continue){
[NSThread sleepForTimeInterval:.025];
[self performSelectorOnMainThread:#selector(whateverTheFunctionIs) waitUntilDone:YES];
}
}
and this would be started by [NSThread detatchNewThreadSelector:#selector(doLoop) toTarget:self withObject:nil]. This is not exactly threadsafe, but you can choose to wrap the boolean into a NSNumber and then do #synchronize on it if you so choose. Alternatively, after I wrote that little snippet I realized it would be better to do a check against the current NSTime instead of sleepForTimeInterval: but you get the point. :)