I am developing a simple game on Xcode 4.4 for iOS 5.1 using storyboards, ARC, and a navigation controller. The app works perfectly on the simulator, but not on the device (iPhone 4 CDMA). So basically, I have a main menu with 3 UIButtons (Play Game, Options, Help). When I click on Play Game and then try to go back to the menu via the navigation controller back button, the app crashes on the device. It is stopped at the following thread:
Thread 1: EXC_BAD_ACCESS (code=1, address=0x70000008)
and pointed to the following:
0x35b4df78: ldr r3, [r4, #8]
There is also a point in my code where I am calling the popToRootViewContoller method. It also crashes here (with same thread error as I would've thought). However, if I comment out the viewWillDisappear method, then I am able to switch back and forth with no issue. The Options and Help screen do not implement the viewWillDisappear method and switch back and forth perfectly on the device.
I have the following under the viewWillDisappear method:
-(void)viewWillDisappear:(BOOL)animated
{
[tmrCountdown invalidate];
[tmrEclapsedTime invalidate];
[tmrMainEnemyMovement invalidate];
[tmrMoveSpawnedEnemies invalidate];
[tmrSpawnEnemies invalidate];
accInc=currPrefs.accelerometerSensitivity;
enemySpeedX=5.0;
enemySpeedY=5.0;
countdown=4;
ecMiliseconds=0;
randTime=0;
stopped=NO;
gameStarted=NO;
}
I call the popToRoot method here:
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex==0)//cancel
{
//called here
[self.navigationController popToRootViewControllerAnimated:YES];
}
else //1 (Play Again)
{
[self reInit];
}
}
Thanks,
Mehul
As I see from code, you are invalidating timers, and this may occur if you try to invalidate an invalid( a non repeating timer
repeats:NO
or released timer. It is not an exception, so you cant catch it with #try block. It is a signal. What you have to do is:
In your timer selector you need to call
[timer release]; // if you have allocated it
timer=nil;
...
Some action
And in your viewWillDisappear
If (timer!=nil) {
[timer invalidate];
timer=nil;
}
Related
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
NSTimer doesn't stop
I am using a NSTimer to update the value of a slider while a audio is playing.
if (sliderUpdateTimer) {
[sliderUpdateTimer invalidate];
sliderUpdateTimer=nil;
}
sliderUpdateTimer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(updateSliderForAudio) userInfo:nil repeats:YES];
once the audio is finished playing i am invalidating the Timer in audioPlayer delegate method.
-(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{
NSLog(#"audioPlayerDidFinishPlaying");
[player release];
audioPlayer=nil;
[sliderUpdateTimer invalidate];
[sliderUpdateTimer release];
sliderUpdateTimer=nil;
}
The delegate method is getting called but the timer is not stopping.... I have only once thread on which i am runing the timer. But still the timer does not stop.... Any help in this regard is welcome...
First of all, you would be over releasing it and should receive EXC_BADACCESS (you're not retaining the timer when you set it, so you shouldn't release it either). Should only call:
[sliderUpdateTimer invalidate];
sliderUpdateTimer=nil;
Since you don't receive a crash, it seems sliderUpdateTimer is nil when calling audioPlayerDidFinishPlaying:. User NSLog or put a breakpoint to check if this is true. If this is the case, you're probably setting it to nil at some point, search for slideUpdateTimer and check where this might occur.
Hey try this thing...
- (void)stopTimer{
[sliderUpdateTimer invalidate];
//don't release it. as it is autoreleased.
//[sliderUpdateTimer release];
sliderUpdateTimer=nil;
}
-(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{
NSLog(#"audioPlayerDidFinishPlaying");
[player release];
audioPlayer=nil;
[self performSelectorOnMainThread:#selector(stopTimer) withObject:nil waitUntilDone:NO];
}
I know you have only single thread but there may be two different run loops
Try after removing the below lines from 1st section of your code
if (sliderUpdateTimer){
[sliderUpdateTimer invalidate];
sliderUpdateTimer=nil;
}
Don't need to call release message on that sliderUpdateTimer object
Because you have created that object with pending autorelease message
Here no need to use these lines.
I hope this will work.
In my previous question
Timer decrement gets varied for each page load
I got the code working well when I used "Invalidate" and "Release". But when i use a back button the same problem repeats. And i need a stop option too..
-(IBAction)back:(id)sender
{
[self dismissModalViewControllerAnimated:YES];
}
-(IBAction)stop:(id)sender
{
[theTimer invalidate];
[theTimer release];
}
this is the code i used here but not working well.
Try using it in this way..
if ([theTimer isValid]) {
[theTimer invalidate];
theTimer=nil;
}
And make sure your method is been called.
whenever we use timer we must to remember to invalidate timer. when you press back button, your view controller hasbeen change but your timer is still working continue so at back button press you must invalidate timer first. and when you alloc timer first check timer is exist or not, if timer is exist you must invalidate timer first.
if(timer)
{
[timer invalidate];
timer = nil;
}
I have created a sample application. It has fetched data from server for every 1 sec and updated the result in UITableView. I had already done. But it crashes my app rapidly. What I do is call NSTimer every sec.
timer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(MainLoop)
userInfo:nil repeats:YES];
In that timer function i have called NSThread function.
-(void)MainLoop
{
if(isPreviousThreadFinished)
{
NSLog(#"Thread Opened");
isPreviousThreadFinished = NO;
[NSThread detachNewThreadSelector:#selector(MainLoopThread) toTarget:self withObject:nil];
}
}
-(void)MainLoopThread
{
MainLoopPool=[[NSAutoreleasePool alloc]init];
//Get data from the server
[self performSelectorOnMainThread:#selector(UpdateTable) withObject:nil waitUntilDone:YES];
[MainLoopPool release];
}
-(void)UpdateTable
{
[self.tableView reloadData];
isPreviousThreadFinished = YES;
NSLog(#"Thread closed");
}
It works fine. And reloads data correctly from the server. I stop NSTimer in viewWillDisappear method. When I go to previous page sometimes it crashes application. In console i have seen the error is given below,
bool _WebTryThreadLock(bool), 0xb2aa410: Tried to obtain the web lock from a thread other than the main thread or the web thread. This may be a result of calling to UIKit from a secondary thread. Crashing now...
What is wrong with my code? Crashes appear randomly.
When I work on my application I discovered very good thing - NSOperation. It's thread safe. You can simply add you operations to the queue without fear to crash. There are a lot of tutorials about NSOperation
http://www.icodeblog.com/2010/03/04/iphone-coding-turbo-charging-your-apps-with-nsoperation/
http://www.cimgf.com/2008/02/16/cocoa-tutorial-nsoperation-and-nsoperationqueue/
I strongly recommend you use NSOperation-s instead of threads in your project.
I have a UITabBarController that nests a UIView-Subclass (ImageViewer) as it's third tab.
In this ImageViewer Subclass I call the viewDidAppear method:
- (void) viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
/* ... start custom code ...
NSLog(#"viewDidAppear tag 1 passed); /* BREAKPOINT 1 here
[myUIActivityIndicator stopAnimating];
NSLog(#"viewDidAppear tag 2 passed); /* BREAKPOINT 2 here
/* ... end custom code ...
}
the method is called automatically, but strangely the view only appears after this method has been processed completely?
When I set breakpoints (1 and 2) as indicated, the processing (upon selecting the tab) stops whilst the previous tab is still showing. Only when clicking continue after the second breakpoint, the view will be displayed. (FYI the NSLogs are carried out immeldiately).
In this case viewDidAppear behaves more like viewWillAppear ....
Any clues what might be going on?
Cheers
If you want to allow the screen to be re-drawn when your view loads, but to trigger some other updating code in -viewDidAppear:, use performSelector:withObject:afterDelay: like this:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self performSelector:#selector(updateUI) withObject:nil afterDelay:0.0];
}
…
- (void)updateUI
{
// Do your UI stuff here
}
When you do it this way, the current event loop will finish quickly, and UIKit will be able to re-draw the screen after your view has loaded. updateUI will be called in the next event loop. This is a good way to get snappy view transitions if you have to perform computationally intensive calculations or updates after a view has loaded.
From the sound of it, if you are actively calling the method, the device might not have time to actually display the view while it is running the "custom code" in your viewDidAppear method. I that case you should let the program call the viewDidAppear method itself.
Your program may also be working on other code which would slow down the appearance of the view, this can be solved using timers. i.e. instead of:
[self otherCode];
you would write:
[NSTimer scheduledTimerWithTimeInterval:.5
target:self
selector:#selector(otherCode)
userInfo:nil
repeats:NO];
you might want to try simply delaying your "custom code" with a timer in this way.
So my menu calls a game with this piece of code:
game = [[Game alloc] init];
[self presentModalViewController:memoryTest animated:FALSE];
A UIViewController then appears with a countdown. The player can go back to the menu DURING the countdown. However when I try this, the countdown keeps running and eventually the game starts, even thought the UIViewController has been dismissed (therefore the UIView has disappeared) in the backToMenu method.
[self.parentViewController dismissModalViewControllerAnimated:FALSE];
I've tried to release the game object in the viewDidAppear method of the menu, but no luck. I thought of having a "quit" BOOL value, so the countdown can check wether or not the player has quit the game, but there must be a better way to release an object AND stop all method calls inside it.
Thanks for helping me out.
CountDown method:
- (void)countDown {
SoundEffect *sound = [[SoundEffect alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"tick" ofType: #"wav"]];
[sound playAndRelease];
if(self.countDownStep > 0) {
feedback.image = [UIImage imageNamed:[NSString stringWithFormat:#"countdown%d.png",self.countDownStep]];
feedback.hidden = FALSE;
[self performSelector:#selector(countDown) withObject:nil afterDelay:0.8];
}
else {
[self displayMessage:self.startMessage];
[self.game performSelector:#selector(start) withObject:nil afterDelay:0.8];
[self performSelector:#selector(hide) withObject:nil afterDelay:0.8];
}
self.countDownStep--;
}
In the viewcontroller's viewWillDisappear, check if the timer is running. If it is, then just invalidate it.
[timerObject invalidate] will stop the timer
How do you handle the countdown? It seems like you want to explicitly void that countdown in the viewWillDisappear method for the UIViewController you mentioned.
I was assuming you would use something like NSTimer. In fact, that might still not be a bad idea. NSTimer can handle re-running your countdown method every 0.8 seconds. If you decrement the value to 0, your countdown has expired. If your view disappears, invalidate the timer in viewWillDisappear.