uitextfield not updating after switching view controllers - ios

I have a uitextlabel that is updated using an nstimer.
When I switch to another view controller (storyboard segue) and back again the text label is no longer updated (returns to default text), even though the timer continues to run.
The timer is writing a value to the uitextlabel which stops working after switching.
NOTE: the updateTimerLabel method continues to output the correct info but the label is not updated.
headerfile
NSString *timerTicksForCounter;
- (void)viewDidLoad
{
[super viewDidLoad];
[self updateTimerLabel];
}
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self updateTimerLabel];
}
- (void) startLastConUpdater
{
lastCTimer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(updateTimer)
userInfo:nil
repeats:YES];
}
-(void) updateTimerLabel
{
NSLog(#"timer: %#", timerTicksForCounter);
if (timerTicksForCounter) {
NSLog(#"timer not null");
mainTimerLabel.text = timerTicksForCounter;
}
}
- (void)updateTimer
{
NSDate *currentDate = [NSDate date];
NSTimeInterval timeInterval = [currentDate timeIntervalSinceDate:stopDate];
NSDate *timerDate = [NSDate dateWithTimeIntervalSince1970:timeInterval];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"mm:ss"];
timerTicksForCounter = [dateFormatter stringFromDate:timerDate];
[self updateTimerLabel];
}

update your textField text in
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self updateLabel];
}
First Declare
NSTimer * countdownTimer;
NSUInteger remainingTicks;
- (void)viewDidLoad
{
[super viewDidLoad];
remainingTicks = 60;
[self updateLabel];
countdownTimer = [NSTimer scheduledTimerWithTimeInterval: 1.0 target: self selector: #selector(handleTimerTick) userInfo: nil repeats: YES];
}
-(void)handleTimerTick
{
remainingTicks--;
[self updateLabel];
if (remainingTicks <= 0) {
[countdownTimer invalidate];
countdownTimer = nil;
}
}
-(void)updateLabel
{
timeLabel.text = [[NSNumber numberWithUnsignedInt: remainingTicks] stringValue];
}

//sent notification
[[NSNotificationCenter defaultCenter] removeObserver:self];
**//Get (Retrive) Notification**
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(receiveTestNotification:)
name:#"TestNotification"
object:nil];
//Get notification method.
- (void) receiveTestNotification:(NSNotification *) notification
{
// [notification name] should always be #"TestNotification"
// unless you use this method for observation of other notifications
// as well.
if ([[notification name] isEqualToString:#"TestNotification"])
NSLog (#"Successfully received the test notification!");
**//Here write your code for update textfield**
}

Related

how to display the congratulation view in game using objective-c

I am developing an App for preschool learning game for kids.So ,i have created ViewController for learning 1-10 numbers .This is my code:
numbersStoreArray = [[NSMutableArray alloc]initWithObjects:#"1.png",#"2.png",#"3.png",#"4.png",#"5.png",#"6.png",#"7.png",#"8.png",#"9.png",#"10.png", nil];
commonFunctionObject = [[SpeechCommonFunctions alloc]init];
commonFunctionObject.isRecordComplete = NO;
counter = 0;
isMicPresent = YES;
_confirmationPopupView.hidden = true;
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:#selector(repeatActionFire) userInfo:nil repeats:NO];
}
-(void)repeatActionFire
{
if(counter >= numbersStoreArray.count)
{
NSLog(#"finished");
[_numbersShowImageView removeFromSuperview];
[_speakerOrMicImageView removeFromSuperview];
UIImageView *congratzView = [[UIImageView alloc]initWithFrame:self.view.frame];
congratzView.image = [UIImage imageNamed:#""];
[self.view addSubview:congratzView];
}
else
{
[commonFunctionObject textToSpeechAction:numbersStoreArray :counter :_numbersShowImageView :_speakerOrMicImageView :isMicPresent];
[NSTimer scheduledTimerWithTimeInterval:10.0 target:self selector:#selector(ActionToCkeckRecordCompletion) userInfo:nil repeats:YES];
}
}
-(void)ActionToCkeckRecordCompletion
{
if(commonFunctionObject.isRecordComplete)
{
_confirmationPopupView.hidden = false;
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)playButtonAction:(id)sender
{
[commonFunctionObject recordPlayAction];
}
- (IBAction)nextButtonAction:(id)sender
{
counter+=1;
_confirmationPopupView.hidden = true;
commonFunctionObject.isRecordComplete = NO;
if(commonFunctionObject.player.playing){[commonFunctionObject.player stop];}
[self repeatActionFire];
}
- (IBAction)retryButtonAction:(id)sender
{
_confirmationPopupView.hidden = true;
commonFunctionObject.isRecordComplete = NO;
if(commonFunctionObject.player.playing){[commonFunctionObject.player stop];}
[self repeatActionFire];
}
After the completion of game,it reaches to code repeatActionFire and it gives finished.Here i used congratzview to display the congratulation to success the game.
My question : i need to display the congralution view and in that two button .
buttons for:1.home
2.retry
i need to do this how to do
What do you mean by displaying notice? If yes try this
1. You Must Define a local notification
- (void)viewWillAppear:(BOOL)animated {
[self setupLocalNotifications];
}
- (void)setupLocalNotifications {
[[UIApplication sharedApplication] cancelAllLocalNotifications];
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
// current time plus 10 secs
NSDate *now = [NSDate date];
NSDate *dateToFire = [now dateByAddingTimeInterval:5];
NSLog(#"now time: %#", now);
NSLog(#"fire time: %#", dateToFire);
localNotification.fireDate = dateToFire;
localNotification.alertBody = #"Time to get up!";
localNotification.soundName = UILocalNotificationDefaultSoundName;
localNotification.applicationIconBadgeNumber = 1; // increment
NSDictionary *infoDict = [NSDictionary dictionaryWithObjectsAndKeys:#"Object 1", #"Key 1", #"Object 2", #"Key 2", nil];
localNotification.userInfo = infoDict;
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
}
2. Register the notification
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UILocalNotification *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (notification) {
[self showAlarm:notification.alertBody];
NSLog(#"AppDelegate didFinishLaunchingWithOptions");
application.applicationIconBadgeNumber = 0;
}
[self.window makeKeyAndVisible];
return YES;
}
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
[self showAlarm:notification.alertBody];
application.applicationIconBadgeNumber = 0;
NSLog(#"AppDelegate didReceiveLocalNotification %#", notification.userInfo);
}
and 3. Display the notifications
- (void)showAlarm:(NSString *)text {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Alarm"
message:text delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
}
I use it to display messages from app, hope it can helps you

Countdown timer in iOS doesn´t work in background

I had implemented a simple countdown with UIDatePicker and NSTimeInterval and it works correctly, but I have the following problem: when I run the app in the Xcode simulator, if I press Ctrl + shift + h, the countdown works in background, but when I run the app on my iPhone 6 and if I press the home button, the countdown stops and it doesn´t work in background.
I have implemented the following notification (AppDelegate.m):
- (void)applicationWillResignActive:(UIApplication *)application {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
[[NSNotificationCenter defaultCenter] postNotificationName:#"didEnterBackground" object:nil];
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
[[NSNotificationCenter defaultCenter] postNotificationName:#"didEnterForeground" object:nil];
}
The code in ViewController.m is:
- (void)viewDidLoad {
self.mensajeCuenta.hidden = YES;
self.botonDetenerCuenta.hidden = YES;
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(enteredBackground:) name:#"didEnterBackground" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(enteredForeground:) name:#"didEnterForeground" object:nil];
estaActivo = false;
cuentaAtras = 0.0;
}
- (void)viewDidUnload
{
[super viewDidUnload];
[[NSNotificationCenter defaultCenter] removeObserver:self];
// Release any retained subviews of the main view.
if ( [tiempo isValid] ) {
[tiempo invalidate];
}
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
} else {
return YES;
}
}
- (IBAction)botonIniciarCuenta:(id)sender {
cuentaAtras = (NSTimeInterval)_vistaContador.countDownDuration;
remainder = cuentaAtras;
//NSLog(#"Total de segundos: %i", remainder);
afterRemainder = cuentaAtras - remainder%60;
//NSLog(#"Total segundos despues restantes: %i", afterRemainder);
tiempo = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(actualizarCuentaAtras) userInfo:nil repeats:YES];
}
- (IBAction)botonDetenerCuenta:(id)sender {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:#"La cuenta atrás se ha parado" message:#"Pulse Iniciar para una nueva cuenta" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:#"Aceptar" style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {}];
[alert addAction:defaultAction];
[self presentViewController:alert animated:YES completion:nil];
[self visibilidadBotones];
[tiempo invalidate];
tiempo = nil;
}
- (void)actualizarCuentaAtras {
self.botonIniciarCuenta.hidden = YES;
self.mensajeCuenta.hidden = NO;
self.tiempoRestante.hidden = NO;
self.botonDetenerCuenta.hidden = NO;
dispatch_async(dispatch_get_main_queue(), ^{
if (afterRemainder >= 0) {
afterRemainder--;
//NSLog(#"Valor restante disminuido: %i", afterRemainder);
int horas = (int)(afterRemainder/(60*60));
int minutos = (int)(((int)afterRemainder/60)- (horas * 60));
int segundos = (int)(((int)afterRemainder - (60 * minutos) - (60 * horas * 60)));
NSString *cadenaTiempo = [[NSString alloc]initWithFormat:#"%02u : %02u : %02u", horas, minutos, segundos];
self.tiempoRestante.text = cadenaTiempo;
if (afterRemainder == 0) {
[tiempo invalidate];
tiempo = nil;
[self visibilidadBotones];
[self enviarAlerta];
}
}
});
}
- (void)enteredBackground:(NSNotification *)notification
{
if (estaActivo) {
[tiempo invalidate];
date = [NSDate dateWithTimeIntervalSinceNow:cuentaAtras];
//NSLog([date description]);
self.notification = [[UILocalNotification alloc] init];
self.notification.fireDate = date;
self.notification.timeZone = [NSTimeZone defaultTimeZone];
self.notification.alertAction = #"timer fired";
self.notification.alertBody = #"timer fired!";
self.notification.soundName = UILocalNotificationDefaultSoundName;
[[UIApplication sharedApplication] scheduleLocalNotification:self.notification];
}
}
- (void)enteredForeground:(NSNotification *)notification
{
if (estaActivo) {
NSTimeInterval newDuration = [self.notification.fireDate timeIntervalSinceNow];
if (newDuration > 0.0) {
cuentaAtras = newDuration;
tiempo = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(actualizarCuentaAtras) userInfo:nil repeats:YES];
} else {
cuentaAtras = 0.0;
//[self.countDownPicker setHidden:NO];
//[self.countDownLabel setHidden:YES];
estaActivo = !estaActivo;
}
[self actualizarCuentaAtras];
[[UIApplication sharedApplication] cancelLocalNotification:self.notification];
}
}
- (void)enviarAlerta {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:#"Notificación enviada" message:#"Puede reiniciar la cuenta atrás" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:#"Aceptar" style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {}];
[alert addAction:defaultAction];
[self presentViewController:alert animated:YES completion:nil];
}
-(void)visibilidadBotones {
self.botonIniciarCuenta.hidden = NO;
self.botonDetenerCuenta.hidden = YES;
self.mensajeCuenta.hidden = YES;
self.tiempoRestante.hidden = YES;
}
#end
The code in ViewController.h is:
#interface ViewController : UIViewController {
int afterRemainder;
int remainder;
NSTimeInterval cuentaAtras;
NSTimer *tiempo;
BOOL estaActivo;
NSDate *date;
}
#property (strong, nonatomic) IBOutlet UIDatePicker *vistaContador;
#property (strong, nonatomic) IBOutlet UILabel *mensajeCuenta;
- (IBAction)botonIniciarCuenta:(id)sender;
#property (strong, nonatomic) IBOutlet UIButton *botonIniciarCuenta;
- (IBAction)botonDetenerCuenta:(id)sender;
#property (strong, nonatomic) IBOutlet UIButton *botonDetenerCuenta;
#property (strong, nonatomic) IBOutlet UILabel *tiempoRestante;
#property (strong, nonatomic) UILocalNotification *notification;
I'm starting to program in iOS, and control processes in the background for me is very complicated, but I don´t understand why the countdown works in Xcode simulator and doesn´t work on my iPhone ?
What´s wrong?
Timers don't run in the background unless you leverage some of the other background modes.
The best way to handle this is to pause it in applicationWillResignActive and make a note of the current time, and then in applicationDidBecomeActive restart it, minus the time elapsed since it was paused.

Updating UILabel with NSTimer stops after switching UIViewControllers

I have two view controllers, both connected via Segue and using Storyboard.
In view controller 1 I have an NSTimer counting up and updating a UILabel.
When I switch to view controller 2 and back to 1 the uilabel is no longer updated.
Here is some code:
headerfile
NSString *timerTicksForCounter;
- (void)viewDidLoad
{
[super viewDidLoad];
[self updateTimerLabel];
}
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self updateTimerLabel];
}
- (void) startLastConUpdater
{
lastCTimer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(updateTimer)
userInfo:nil
repeats:YES];
}
-(void) updateTimerLabel
{
NSLog(#"timer: %#", timerTicksForCounter);
if (timerTicksForCounter) {
NSLog(#"timer not null");
mainTimerLabel.text = timerTicksForCounter;
}
}
- (void)updateTimer
{
NSDate *currentDate = [NSDate date];
NSTimeInterval timeInterval = [currentDate timeIntervalSinceDate:stopDate];
NSDate *timerDate = [NSDate dateWithTimeIntervalSince1970:timeInterval];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"mm:ss"];
timerTicksForCounter = [dateFormatter stringFromDate:timerDate];
[self updateTimerLabel];
}
What do you mean it's no longer updated ? Does this mean you lose what was displayed before switching or it doesn't update anymore. If it's not updating anymore it's because you don't start the timer in the appropriate method. You could do something like :
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self startLastConUpdater];
}
This should solve both issues I mentioned above.

Pausing a NSTimer/Stopwatch [duplicate]

This question already has an answer here:
Adding pause functionality for NSTimer
(1 answer)
Closed 9 years ago.
In the project I am working on I need to have a stopwatch that will pause and continue. So far All of the basic functions work, but I have not been able to find a way to pause the timer and re-start it. FYI, I have already checked the other postings and they didn't work. Code:
.h:
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#interface Timer : UIViewController <AVAudioRecorderDelegate, AVAudioPlayerDelegate>
{
AVAudioRecorder *recorder;
AVAudioPlayer *player;
}
#property (weak, nonatomic) IBOutlet UIButton *recordPauseButton;
#property (weak, nonatomic) IBOutlet UIButton *stopButton;
#property (weak, nonatomic) IBOutlet UILabel *stopwatchLabel;
-(IBAction)recordPauseTapped:(id)sender;
-(IBAction)stopTapped:(id)sender;
#end
.m:
#import "Timer.h"
#interface SongIdeasRecording ()
#property (strong, nonatomic) NSTimer *stopWatchTimer; // Store the timer that fires after a certain time
#property (strong, nonatomic) NSDate *startDate; // Stores the date of the click on the start button
#end
#implementation Timer
#synthesize stopButton, playButton, recordPauseButton, stopwatchLabel;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)updateTimer
{
// Timer is 1/10 of a second so thats what we add to stopwatch
NSTimeInterval timeInterval = 0.1;
// Create a date formatter
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"HH:mm:ss.SSS"];
[dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0.0]];
// Take the time currently displayed on the stopwatch and add the time interval to it
NSDate *oldDate = [dateFormatter dateFromString:self.stopwatchLabel.text];
NSDate *newDate = [oldDate dateByAddingTimeInterval:timeInterval];
//Get a string representation of the new date
NSString *timeString = [dateFormatter stringFromDate:newDate];
self.stopwatchLabel.text = timeString;
}
- (IBAction)recordPauseTapped:(id)sender {
self.startDate = [NSDate date];
// Create the stop watch timer that fires every 100 ms
self.stopWatchTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/10.0
target:self
selector:#selector(updateTimer)
userInfo:nil
repeats:YES];
// Stop the audio player before recording
if (player.playing) {
[player stop];
}
if (!recorder.recording) {
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setActive:YES error:nil];
// Start recording
[recorder record];
[recordPauseButton setTitle:#"Pause" forState:UIControlStateNormal];
} else {
// Pause recording
[self.stopWatchTimer invalidate];
self.stopWatchTimer = nil;
[self updateTimer];
[recorder pause];
[recordPauseButton setTitle:#"Record" forState:UIControlStateNormal];
}
[stopButton setEnabled:YES];
[playButton setEnabled:NO];
}
- (IBAction)stopTapped:(id)sender {
[recorder stop];
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setActive:NO error:nil];
[self.stopWatchTimer invalidate];
self.stopWatchTimer = nil;
[self updateTimer];
}
- (void) audioRecorderDidFinishRecording:(AVAudioRecorder *)avrecorder successfully: (BOOL)flag{
[recordPauseButton setTitle:#"Record" forState:UIControlStateNormal];
[stopButton setEnabled:NO];
[playButton setEnabled:YES];
}
- (IBAction)playTapped:(id)sender {
if (!recorder.recording){
player = [[AVAudioPlayer alloc] initWithContentsOfURL:recorder.url error:nil];
[player setDelegate:self];
[player play];
self.startDate = [NSDate date];
stopwatchLabel.text = #"00:00:00.000";
self.stopWatchTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/10.0
target:self
selector:#selector(updateTimer)
userInfo:nil
repeats:YES];
}
}
- (void) audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
[self.stopWatchTimer invalidate];
self.stopWatchTimer = nil;
[self updateTimer];
}
#end
In your case, you are calculating the value of the stopwatch label with the NSDate that the record button was originally pressed. There is no way to pause the timer in this way, as for every time you recalculate the value of the stopwatch label, it will reflect the original date of which the record button was pressed. I would recommend changing this method to something like this:
- (void)updateTimer
{
// Timer is 1/10 of a second so thats what we add to stopwatch
NSTimeInterval timeInterval = 0.1;
// Create a date formatter
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"HH:mm:ss.SSS"];
[dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0.0]];
// Take the time currently displayed on the stopwatch and add the time interval to it
NSDate *oldDate = [dateFormatter dateFromString:self.stopwatchLabel.text];
NSDate *newDate = [oldDate dateByAddingTimeInterval:timeInterval];
//Get a string representation of the new date and BOOM POW.
NSString *timeString = [dateFormatter stringFromDate:newDate];
self.stopwatchLabel.text = timeString;
}
Have not tested this but I hope it works. I wouldn't be surprised if there were some syntax issues too. Also, make sure the string that is in self.stopwatchLabel.text follows the format to start (Ex. 00:00:00.000).
Try commenting your code inside - (void) audioPlayerDidFinishPlaying:(AVAudioPlayer *)player.
My guess is that in - (IBAction)recordPauseTapped:(id)sender you're calling [player stop], which triggers - (void) audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag which invalidates your new timer.
- (IBAction)recordPauseTapped:(id)sender {
if ([stopwatchLabel.text isEqual: #"00:00:00.000"]) {
self.startDate = [NSDate date];
// Create the stop watch timer that fires every 100 ms
self.stopWatchTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/10.0
target:self
selector:#selector(updateTimer)
userInfo:nil
repeats:YES];
// Stop the audio player before recording
if (player.playing) {
[player stop];
}
if (!recorder.recording) {
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setActive:YES error:nil];
// Start recording
[recorder record];
[recordPauseButton setTitle:#"Pause" forState:UIControlStateNormal];
}
}else {
// Pause recording
[self.stopWatchTimer invalidate];
self.stopWatchTimer = nil;
[self updateTimer];
[recorder pause];
[recordPauseButton setTitle:#"Record" forState:UIControlStateNormal];
}
[stopButton setEnabled:YES];
[playButton setEnabled:NO];
}

iOS Logic Unit Test target crashes after unit test passes with NSTimer

I want a timer class that can post messages to a delegate when there are 1/2/3 seconds to go.
My test target consistently crashes.
iOS logic unit test target.
Tests class that times a duration using a repeating NSTimer
One test with no asserts. The test passes, but then the target crashes with:
/Developer/Tools/RunPlatformUnitTests.include: line 415: 770 Bus error "${THIN_TEST_RIG}" "${OTHER_TEST_FLAGS}" "${TEST_BUNDLE_PATH}"
It seems to me that it's some kind of memory allocation error, but I can't figure out what I'm missing. The problem is associated with the stop timer routine somehow. It's only when the timer runs out that the target crashes.
Things I've tried
Build and Analyze - no errors reported
Remove -framework and UIKit from the linker flags
Removing dealloc - this has no effect
Test Code
-(void)testGivenThreeSecondDurationAtOneSecondDelegateShouldBeToldToShowGreenCard {
JGTimerController *timer = [JGTimerController timerWithDurationValue:1 delegate:nil];
[timer startTimer];
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.1]];
}
Class Code
#interface JGTimerController : NSObject {
NSNumber *duration;
NSTimer *timer;
id <NSObject, JGTimerControllerDelegate> _delegate;
}
#property (nonatomic, retain) NSNumber *duration;
... public methods...
#end
#implementation JGTimerController
#synthesize duration;
+(JGTimerController *)timerWithDurationValue:(NSUInteger)durationValue delegate:(id <JGTimerControllerDelegate>)delegate_ {
JGTimerController *instance = [[[JGTimerController alloc] initWithDurationValue:durationValue delegate:delegate_] autorelease];
return instance;
}
-(JGTimerController *)initWithDurationValue:(NSUInteger)durationValue delegate:(id <JGTimerControllerDelegate>)delegate_ {
self = [super init];
timer = nil;
[self setDurationValue:durationValue];
_delegate = delegate_;
return self;
}
-(NSUInteger)durationValue {
NSNumber *result = [self duration];
return result ? [result intValue] : 0;
}
-(void)setDurationValue:(NSUInteger)value_ {
[self setDuration:[NSNumber numberWithInt:value_]];
}
-(BOOL)stopTimerAtZeroDuration:(NSTimer *)timer_ {
if ([self durationValue] == 0) {
[self stopTimer];
return YES;
}
return NO;
}
-(void)startTimer {
if ([self stopTimerAtZeroDuration:nil])
return;
timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(timerDidCountDownByASecond:) userInfo:nil repeats:YES];
}
-(void)stopTimer {
if ([self durationValue] == 0 && [_delegate conformsToProtocol:#protocol(JGTimerControllerDelegate)])
[_delegate showRedCard];
[timer invalidate];
[timer release];
}
-(BOOL)timerIsRunning {
return (timer != nil);
}
-(void)timerDidCountDownByASecond:(NSTimer *)timer_ {
[self setDurationValue:[self durationValue] - 1];
[self stopTimerAtZeroDuration:timer_];
}
-(void)dealloc {
[_delegate release];
[timer release];
[duration release];
[super dealloc];
}
#end
There should be no [_delegate release] in dealloc because you did not retain it.
Likewise, timer should not be released. NSTimer is like every other NSObject, if you did not alloc, copy or retain, you do not need to release.

Resources