Release NSObject and stop all method calling - iphone

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.

Related

How to prevent UIImage from continually replaying an animation

I am making a game and currently my "player" is animating as I've implemented (which is wrong at the moment) and it continually runs the animation from start to start because it always restarts from the beginning when the function is called. What is the best way to prevent this and let the animation run all the way through? Here is the code I'm using at the moment:
- (void) startAnimatingLeft
{
NSArray *images = [NSArray arrayWithObjects:img9,img10,img11,img12,img13,img14,img15,img16, nil];
[imageView setAnimationImages:images];
[imageView startAnimating];
animateTimer = [NSTimer scheduledTimerWithTimeInterval:0.6 target:self selector:#selector(nothingMovingLeft) userInfo:nil repeats:NO];
}
From UIImage documentation:
startAnimating
This method always starts the animation from the first image in the list.
So you shouldn't call this method if the image is already animating. You can call isAnimating method to check if image is animating.

Variables falling out of scope

I have a subclass of AVAudioPlayer and within that subclass I have a method for stopping the current player and (for reasons I won't explain) manually calling audioPlayerDidFinishPlaying like this:
// Handles stopping the player and calling audioPlayerDidFinishPlaying
- (void) stopPlayerForTimedRepeat {
// Stop the player
[self stop];
// Manually call the audio player callback
EditPlayListViewController *playlistController = [[EditPlayListViewController alloc] init];
[playlistController audioPlayerDidFinishPlaying:self successfully:YES];
[playlistController release];
}
However, when I call audioPlayerDidFinishPlaying manually like this, all of my variables in the original EditPlaylistViewController fall out of scope.
How do I avoid this so that I still have access to all of my original variables?
I figured out a better way to do this without manually calling audioPlayerDidFinishPlaying so that all the variables will still be within scope.
// Handles stopping the player and calling audioPlayerDidFinishPlaying
- (void) stopPlayerForTimedRepeat {
// Fast forward the call to the end, which will also call audioPlayerDidFinishPlaying
[self setCurrentTime:[self duration]];
}
The variable scope loses because you are creating a new object EditPlayListViewController *playlistController = [[EditPlayListViewController alloc] init]; in the method after stopping it.
Please declare the player in your .h file (like this EditPlayListViewController *playlistController; and allocate it in your viewDidLoad method (like playlistController = [[EditPlayListViewController alloc] init];.
and change your method like this,
- (void) stopPlayerForTimedRepeat
{
// Stop the player
[self stop];
// Manually call the audio player callback
[playlistController audioPlayerDidFinishPlaying:self successfully:YES];
[playlistController release];
}
Hope,it'll work for you, if not please tell me.

ARC - How to get rid of stuff

I just decided to run a test upon suspecting some behavior:
In my view controller I have a new game method which does this:
-(void) newClassicGame {
if (classicGame!=nil) {
[classicGame saveStats];
}
classicGameController = nil;
classicGameController = [[RegularGameViewController alloc] initWithPowerMode:NO ];
classicGame = nil;
classicGame = [[RegularGame alloc] initWithViewController:classicGameController];
classicGameController.game = classicGame; // game property is __weak to avoid
//retain cycles.
[classicGame startGame];
}
and to test if objects get destroyed with ARC, in the startGame method of classicGame, I put this:
-(void) startGame
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(#"once");
[NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:#selector(repeat) userInfo:nil repeats:YES];
});
//other stuff..
}
-(void) repeat {
NSLog(#"I repeat");
}
for those who are not familiar with dispatch_once method, it's just a method that makes sure that the block gets called exactly once, and no more, during the life time of an application.
I start my app, hit new game and game starts - I see the log "I repeat" repeating every 3 seconds".. Then I go back to the menu and hit new game again. Strangely, the log keeps on repeating, indicating that my classicGame instance is not completely destroyed and the timer is still running somewhere. Any ideas what is happening here?
The timer that you create retains its target (self, the game), so your game object won't be deallocated at least until the timer is invalidated.

objective-c: Animate button before timer ends

I'm working on a very simple iPhone game that involves choosing the right colored button as many times in a row based on a randomized voice prompt. I have it set up so that if the button is one color and gets clicked, it always goes to a hard-coded color every time (e.g. if you click red, it always turns blue). The color change method is set up in an IBOutlet. I have a timer set up in a while loop, and when the timer ends it checks if the player made the right selection. The problem is that the button color change does not occur until after the timer runs out, and this causes a problem with the method used to check the correct answer. Is there a way to make this color change happen instantly? From what I've searched I know it has something to do with storyboard actions not occurring until after code executes, but I haven't found anything with using a timer. Here is a section of the method that calls the timer if the answer is correct:
BOOL rightChoice = true;
int colorNum;
NSDate *startTime;
NSTimeInterval elapsed;
colorNum = [self randomizeNum:middle];
[self setTextLabel:colorNum];
while (rightChoice){
elapsed = 0.0;
startTime = [NSDate date];
while (elapsed < 2.0){
elapsed = [startTime timeIntervalSinceNow] * -1.0;
NSLog(#"elapsed time%f", elapsed);
}
rightChoice = [self correctChoice:middleStatus :colorNum];
colorNum = [self randomizeNum:middle];
}
One of two things stood out
You're using a while loop as a timer, don't do this - the operation is synchronous.
If this is run on the main thread, and you code doesn't return, your UI will update. The mantra goes: 'when you're not returning you're blocking.'
Cocoa has NSTimer which runs asynchronously - it is ideal here.
So let's get to grips with NSTimer (alternatively you can use GCD and save a queue to an ivar, but NSTimer seems the right way to go).
Make an ivar called timer_:
// Top of the .m file or in the .h
#interface ViewController () {
NSTimer *timer_;
}
#end
Make some start and stop functions. How you call these is up to you.
- (void)startTimer {
// If there's an existing timer, let's cancel it
if (timer_)
[timer_ invalidate];
// Start the timer
timer_ = [NSTimer scheduledTimerWithTimeInterval:5.0
target:self
selector:#selector(onTimerFinish:)
userInfo:nil
repeats:NO];
}
- (void)onTimerFinish:(id)sender {
NSLog(#"Timer finished!");
// Clean up the timer
[timer_ invalidate];
timer_ = nil;
}
- (void)stopTimer {
if (!timer_)
return;
// Clean up the timer
[timer_ invalidate];
timer_ = nil;
}
And now
Put your timer test code in the onTimerFinish function.
Make an ivar that stores the current choice. Update this ivar when a choice is made and make the relevant changes to the UI. Call stopTimer if the stop condition is met.
In the onTimerFinished you can conditionally call and startTimer again if you desire.
Hope this helps!

Timer decrement gets varied for each page load when using back button

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;
}