Leaked Object NSDate: ResumeTimer & PauseTimer - iphone

Tested app in Instrumens for memory leak getting multiple leaks for using multiple times pauseTimer and resumeTimer.
Defined in m file
NSDate *pauseStart, *previousFireDate;
-(void)pauseTimer{
pauseStart = [[NSDate dateWithTimeIntervalSinceNow:0] retain];
previousFireDate = [[timer fireDate] retain];
[timer setFireDate:[NSDate distantFuture]];
}
-(void)resumeTimer{
float pauseTime = -1*[pauseStart timeIntervalSinceNow];
//[timer setFireDate:[previousFireDate initWithTimeInterval:pauseTime sinceDate:previousFireDate]];
[timer setFireDate:[NSDate dateWithTimeInterval:pauseTime sinceDate:previousFireDate]];
}
how to fix this memory leak for pauseTimer and resumeTimer. User can use PauseTimer and resumeTimer multiple times.
Appreciate help.
Thanks.

You are retaining the values for pauseStart and previousFireDate each time the pauseTimer method is fired. You need to release the previous values before retaining the new values in order to avoid a memory leak.
Easiest solution is to create two retained properties for those values (possibly in a class extension), so when you set new values, the release is handled for you. (Just don't forget to release the values in your dealloc method to also avoid leaks.)

You can use NSTimer's isValid property and invalidate method.
-(void)pauseTimer
{
if ( !timer.isValid )
return;
// remember your fire date
[timer invalidate];
}
-(void)resumeTimer
{
if ( timer.isValid )
return;
// schedule your timer to new date
// release old date
}

Related

NSTimer crashes with bad access

I have the following method to update a label which show a simple time up
-(void)updateTimeLabel:(NSTimer *)timer{
NSInteger secondsSinceStart = (NSInteger)[[NSDate date] timeIntervalSinceDate:_startTime];
NSInteger seconds = secondsSinceStart % 60;
NSInteger minutes = (secondsSinceStart / 60) % 60;
NSInteger hours = secondsSinceStart / (60 * 60);
NSString *result = nil;
if (hours > 0) {
result = [NSString stringWithFormat:#"%02d:%02d:%02d", hours, minutes, seconds];
}
else {
result = [NSString stringWithFormat:#"%02d:%02d", minutes, seconds];
}
_totalTime = result;
_totalTimeLabel.text = result;
}
I then call this as the action to a button:
-(IBAction) startTimer{
_startTime = [NSDate date];
_walkRouteTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(updateTimeLabel:) userInfo:nil repeats:YES];
[_walkRouteTimer fire];
}
But when I run the app I get a bad access error and the app crashes, can anyone help me with this?
Thanks in advance
Are you using ARC? If not, _startTime = [NSDate date]; this line will cause your problem. [NSDate date] returned an autorelease object and _startTime will not hold it if you are not using ARC(or using ARC but declared _startTime as weak).
If so, try to add a retain to it
-(IBAction) startTimer{
//_startTime = [NSDate date]
_startTime = [[NSDate date] retain];
_walkRouteTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(updateTimeLabel:) userInfo:nil repeats:YES];
[_walkRouteTimer fire];
}
And when you finished your timer, after calling of [_walkRouteTimer invalidate], call [_startTime release].
Or even simpler, if you use property for startTime and declared it as retain. Just use dot notation:
-(IBAction) startTimer{
//_startTime = [NSDate date]
self.startTime = [NSDate date];
_walkRouteTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(updateTimeLabel:) userInfo:nil repeats:YES];
[_walkRouteTimer fire];
}
...
//After [_walkRouteTimer invalidate]
self.startTime = nil;
Try adding an exception breakpoint to see which line is crashing:
1) Click on the breakpoint tab (second from the right)... kinda looks like a right pointing arrow or "next" button
2) Click on the "+" in the bottom left corner of the tab menu
3) Select "Add Exception Breakpoint"
4) (Optional) Select "Exception" drop down and change to "Objective-C"
5) Select "Done"
6) Run your code again and try to generate the crash... when you do, it will hopefully be caught by this breakpoint, and you'll see which line is crashing and be able to fix it
Good luck

NSTimer Pause/ resume leak

I wanted to be able to Pause/ Resume my NSTimer and found this below answer:
NSDate *pauseStart, *previousFireDate;
-(void) pauseTimer:(NSTimer *)timer {
pauseStart = [[NSDate dateWithTimeIntervalSinceNow:0] retain];
previousFireDate = [[timer fireDate] retain];
[timer setFireDate:[NSDate distantFuture]];
}
-(void) resumeTimer:(NSTimer *)timer {
float pauseTime = -1*[pauseStart timeIntervalSinceNow];
[timer setFireDate:[previousFireDate initWithTimeInterval:pauseTime sinceDate:previousFireDate]];
[pauseStart release];
[previousFireDate release];
}
which works fine. However when testing my App for Leaks, it tells me that I get a leak in here:
[timer setFireDate:[previousFireDate initWithTimeInterval:pauseTime sinceDate:previousFireDate]];
Can anyone help me? You can see from the code i used that pauseStart and previousFireDate are retained in the pauseTime method and released in the resumeTimer method.
Many thanks
Sam
You should never call init (or any of the other methods in the same family) on an object twice. (Here's why). Change that line to this:
[timer setFireDate:[NSDate dateWithTimeInterval:pauseTime sinceDate:previousFireDate]];
and you'll be fine.

Objective C instance variable initialization in a method

Did any body get this issue?
If I need an instance variable, not as a property, and initialize this variable in a method, then when I need it, it is already released. It happens for autoreleased objects. What is the reason for this?
Usually instance variable should have the whole lifetime of the class object. But it seems if the variable is local to a function, and its a autorelease object, it is released when the function exits.
MyClass.h
#interface MyClass:UIViewController {
NSDate * date;
}
MyClass.m
#implementation MyClass {
- (void) anInit {
date = [NSDate date];
}
- (void) useDate {
NSLog (#"%#", date);
// here date is already release, and get bad access.
}
}
You need to retain date.
An autoreleased object will be released when the autorelease pool is next drained. When this happens has nothing to do with the lifecycle of your object.
Your implementation should look like this:
#implementation MyClass {
- (void) anInit {
date = [[NSDate date] retain]; // or [[NSDate alloc] init]
}
- (void) useDate {
NSLog (#"%#", date);
}
- (void) dealloc {
[date release];
[super dealloc];
}
}
[NSDate date] is a Convenience Constructor and is autoreleased, you need to add a retain call. Also make sure anInit is only called once or you will create a memory leak without calling [date release] first.
- (void) anInit {
date = [[NSDate date] retain];
}

how to pause and resume NSTimer in iphone

hello
I am developing small gameApp.
I need to pause the timer,when user goes to another view [say settings view].
when user comes back to that view , I need to resume the timer.
can anybody solve this issue ...
Thanks in Advance...
NSTimer does not give you the ability to pause it. However, with a few simple variables, you can create the effect yourself:
NSTimer *timer;
double timerInterval = 10.0;
double timerElapsed = 0.0;
NSDate *timerStarted;
-(void) startTimer {
timer = [NSTimer scheduledTimerWithTimeInterval:(timerInterval - timerElapsed) target:self selector:#selector(fired) userInfo:nil repeats:NO];
timerStarted = [NSDate date];
}
-(void) fired {
[timer invalidate];
timer = nil;
timerElapsed = 0.0;
[self startTimer];
// react to timer event here
}
-(void) pauseTimer {
[timer invalidate];
timer = nil;
timerElapsed = [[NSDate date] timeIntervalSinceDate:timerStarted];
}
This has been working out quite well for me.
You can't pause a timer. However, when the user goes to the settings view, you can save the fireDate of the timer and also the current date. After this you invalidate the timer and let the user do his/her stuff.
Once he/she switches back to the game, you create a new timer object and set the fire date to the old fire date plus the time the user was in the menu (oldTime + currentTime).
you can use this code to implement pause and resume functionality in NSTimer
//=========new timer update method=============
-(void) updateTimer {
NSDate *currentDate = [NSDate date];
NSTimeInterval timeInterval = [currentDate timeIntervalSinceDate:startDate];
NSDate *timerDate = [NSDate dateWithTimeIntervalSince1970:timeInterval];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"mm:ss.S"];
[dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0.0]];
NSString *timeString = [dateFormatter stringFromDate:timerDate];
lblMessage.text = timeString;
pauseTimeInterval = timeInterval;
}
-(IBAction) startBtnPressed:(id)sender
{
//=============================new update with start pause==================
if(running == NO) {
running = YES;
startDate = [NSDate date] ;
startDate = [[startDate dateByAddingTimeInterval:((-1)*(pauseTimeInterval))] retain];
stopWatchTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/10.0 target:self selector:#selector(updateTimer) userInfo:nil repeats:YES];
}
else {
running = NO;
[stopWatchTimer invalidate];
stopWatchTimer = nil;
[self updateTimer];
}
}
declare in .h file NSDate startDate;
NSTimeInterval pauseTimeinterval;
and in viewdidload pausetimeInterval=0.0; good luck
You can't pause an NSTimer. You can, however invalidate it and create a new one when needed.
on Start -
startNewCapture = [NSDate date];
on Pause -
captureSessionPauseDate = [NSDate date];
captureSessionTimeInterval = [captureSessionPauseDate timeIntervalSinceDate:startNewCapture];
on Resume -
NSDate *dateNow = [NSDate date];
startNewCapture = [NSDate dateWithTimeInterval:-captureSessionTimeInterval sinceDate:dateNow];
- (IBAction)pauseResumeTimer:(id)sender {
if (timerRunning == NO) {
timerRunning = YES;
[pauseTimerBtn setTitle:#"Resume" forState:UIControlStateNormal];
NSString *stringVal = [NSString stringWithFormat:#"%#",timeTxt.text];
stringVal = [stringVal stringByReplacingOccurrencesOfString:#":" withString:#"."];
float tempFloatVal = [stringVal floatValue];
int minuteValue = floorf(tempFloatVal);
float tempSecVal = [stringVal floatValue] - floorf(tempFloatVal);
int secondVal = tempSecVal*100;
minuteValue = minuteValue*60;
oldTimeValue = minuteValue + secondVal;
[timer invalidate];
timer = nil;
}
else
{
timerRunning = NO;
[pauseTimerBtn setTitle:#"Pause" forState:UIControlStateNormal];
startDate = [NSDate date];
timer = [NSTimer scheduledTimerWithTimeInterval:0.25 target:self selector:#selector(timer:) userInfo:nil repeats:YES];
}
}
- (void)runTimer:(NSTimer *)timer {
NSInteger secondsAtStart = (NSInteger)[[NSDate date] timeIntervalSinceDate:startDate];
secondsAtStart = secondsAtStart + oldTimeValue;
NSInteger seconds = secondsAtStart % 60;
NSInteger minutes = (secondsAtStart / 60) % 60;
NSInteger hours = secondsAtStart / (60 * 60);
NSString *result = nil;
result = [NSString stringWithFormat:#"%02ld:%02ld",(long)minutes,(long)seconds];
timeTxt.text = result;
}
Did you end up figuring it out? I saw that you said that you cannot invalidate your timer when you go into another view. Can you explain what you mean by that? NSTimers cannot be paused, and methods to simulate pausing them usually involve invalidating them. You can then simulate "unpausing" by creating a new timer that will then start up again. This will simulate a timer being paused and unpaused.
For people who would like a potentially more convenient method, I wrote a controller class that can conveniently handle pausing and unpausing timers. You can find it here: https://github.com/LianaChu/LCPausableTimer
You can use the controller class that I wrote to create new timers, and then you can pause and unpause the timers by call the methods "pauseTimer" and "unpauseTimer" on the controller class.
I would greatly appreciate any comments or feedback like how you used it, what changes you would like to see, or any features you would like me to add. Please don't hesitate to reply with your comments here, or post on the issues tab on the Github page.

NSDate assign problem

I would like to assign a date from one view controller to another
-(void) setCurrentDate:(NSDate newDate){
self.currentDate = newDate;
[self updateView];
}
While debugging I see the currentDate value out of scope and the application crashes with EXC_BAD_ACCESS.
Any help will be appreciated.
Besides that your setter should take NSDate by pointer (all class-type instances are passed by pointer in Objective-C), you are recursively calling the setter:
self.currentDate = foo results in [self setCurrentDate:foo] being called.
Correctly it should look e.g. like this (assuming a nonatomic, retain property):
- (void)setCurrentDate:(NSDate *)newDate {
if (currentDate != newDate) {
[currentDate release];
[newDate retain];
currentDate = newDate;
[self updateView];
}
}
Alternatively name that method different from the setter so you can use the synthesized setter:
- (void)updateDate:(NSDate *)newDate {
self.currentDate = newDate;
[self updateView];
}
You need to pass the pointer to date. Something like this:
-(void) setCurrentDate:(NSDate* newDate){
[self.currentDate release];
self.currentDate = newDate;
[self.currentDate retain];
[self updateView];
}
Of course, your currentDate class variable should also be a NSDate pointer. It will be even better if you use a property instead of a custom made setter.
Possibly, you need to retain newDate or copy it, if it's possible.
What I mean:
You create newDate
You call setCurrentDate
You release newDate
[self updateView] try to use it and fails because it is already released.
You also can try NSZombieEnabled to catch this kind of bugs.
In your method name, you use (NSDate date). You forgot to include the "*", which makes it a pointer. The correct code should be
-(void) setCurrentDate:(NSDate *newDate){ // Notice the star after NSDate
    self.currentDate = newDate;
    [self updateView];
}