I created a clock for my app that I’m working on that starts off normal (counting by one second each time), but then freezes every two seconds, then every four, then eight, etc. until it reaches twenty and the app crashes, with no error in xcode showing, not even a Recieved Memory Warning.
I went to Instruments and found that when using my clock, my app’s memory was increasing at an alarming rate (although, any rate is alarming to me). The problem is, I can’t figure out what it is exactly that is causing the memory to go so high. The app is running on ARC, so I know that I didn’t forget to release something. The only thing I can think of is that each time the number is loaded it’s stored into memory and never released. How that is, I have no idea, and how to fix it I have even less of an idea. Please let me know what I can do. Here is my code:
-(void)updateTime {
calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
unsigned unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
updateTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateTime) userInfo:nil repeats:YES];
currentTime = [NSDate date];
dateComponents = [calendar components:unitFlags fromDate:currentTime];
year = [dateComponents year];
month = [dateComponents month];
day = [dateComponents day];
hour = [dateComponents hour];
minute = [dateComponents minute];
second = [dateComponents second];
if (hour >= 12) {
lblAMPM.text = @"PM";
} else {
lblAMPM.text = @"AM";
}
//if (isTwelveHour == YES) {
//make a 12 hrs clock instead of 24
if (hour >= 12) {
hour = 12 - (24-hour);
}
//}
//if midnight, show hour as 12 instead of 0
if (hour == 0) {
hour = 12;
}
if (second < 10) {
seconds = [NSString stringWithFormat:@"0%i", second];
} else {
seconds = [NSString stringWithFormat:@"%i", second];
}
if (minute < 10) {
lblTime.text = [NSString stringWithFormat:@"%i:0%i:%@", hour, minute, seconds];
} else {
lblTime.text = [NSString stringWithFormat:@"%i:%i:%@", hour, minute, seconds];
}
[lblDate setText:[NSString stringWithFormat:@"%d/%d/%d", month, day, year]];
}
- (void) updateDateFirst:(int)firstComponent setSecond:(int)secondComponent{
calendar = [NSCalendar currentCalendar];
unsigned unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
updateTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateTime) userInfo:nil repeats:YES];
currentTime = [NSDate date];
dateComponents = [calendar components:unitFlags fromDate:currentTime];
year = [dateComponents year];
month = [dateComponents month];
day = [dateComponents day];
[lblDate setText:[NSString stringWithFormat:@"%d/%d/%d", firstComponent, secondComponent, year]];
}
- (void)viewDidUnload {
lblTime = nil;
lblAMPM = nil;
lblDate = nil;
[super viewDidUnload];
}
I am calling updateTimer in viewDidAppear, just as an FYI.
Every time
updateTimeis called it creates a new timer and adds it to the current run loop when you call:The run loop retains these timers. So although you keep replacing the
NSTimerpointed to by your ivarupdateTimerthey are neverdealloc‘ed. ARC manages eachNSTimerassigned toupdateTimerso they have matchingretain/releasecalls on them. However, theretainby the run loop when they are first added is never paired with a matchingreleasesince the timers are repeating and never invalidated. So the sequence goes like this:- (void) updateDateFirst:(int)firstComponent setSecond:(int)secondComponentto start things off and it creates anNSTimer.updateTime, which creates a new timer. The original timer is repeating so it will fire again in 1 second. Now you have 2 timers.updateTime, and create 2 new timers. Now you have 4 timers….The standard way you would do this is just to create 1 repeating timer at the beginning and not in each call to
updateTimer. Then when you are done, you call:This stops and frees the timer.