This bug has been in my code for days - I've been trying to solve it but to no avail. I've done extensive debugging, on simulator and on a device, logging, Crashlytics throwing up the crash log, and it has to do with my NSTimer
being sent a message after deallocation. I'll explain further.
This NSTimer
controls my UIToolbar
label on some view controllers, and it keeps updating the label to show the relative time from when the user refreshed the UITableView
. Upon load, and with every refresh, it shows Updated just now
and then if the user doesn't refresh/go to another VC, it keeps updating every 60 seconds. So, after a minute, it would show Updated 1 minute ago
. The NSTimer
triggers a method that does the UILabel
updating. On user refresh (by pulling on the refresh control), this method is invoked manually inside my getAllItems
(that's why I pass nil
).
My NSTimer
is defined in my superclass as:
@property (nonatomic, weak) NSTimer *updateDateTimer;
This is the code that get the items into my tableview:
- (void)getAllItems
{
KILL_TIMER(self.updateDateTimer);
self.updateLabel = [Formatters boldLabelWithLabel:self.updateLabel
withText:@"Checking for items..."
withFont:[UIFont fontWithName:kOpenSansBold size:15.0f]];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self fetchObjectsWithRequest:self.request];
sleep(1);
dispatch_async(dispatch_get_main_queue(), ^{
[self.refreshControl performSelector:@selector(endRefreshing) withObject:nil afterDelay:0];
self.updatedDate = [NSDate date];
[self updateTimer:nil];
self.updateDateTimer = [NSTimer scheduledTimerWithTimeInterval:kToolbarUpdateLabelInterval
target:self
selector:@selector(updateTimer:)
userInfo:nil repeats:YES];
});
});
}
and KILL_TIMER(q)
is defined, in my superclass, as:
#define KILL_TIMER(q) if (q) {DDLogVerbose(@"KILLING TIMER: %@", q); [q invalidate]; q=nil;}
This define is invoked in 2 methods:
a) in my getAllItems
method: so when I (re)load items (by invoking that method in viewDidLoad
or by user refreshing the table view), I always try to kill the current timer that's active, show the 'checking for items', fetch the items, show 'updated just now' and then schedule the timer to execute every 60 seconds.
b) on each viewWillDisappear`, as such:
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
KILL_TIMER(self.updateDateTimer);
}
However, if I hit the home button, and then reopen the app, it generally (not always, only if I had switched view controllers) crashes with the following message (with NSZombies enabled):
2013-07-11 17:21:37.576 App[3923:907] *** -[ItemCDTVC setUpdateDateTimer:]: message sent to deallocated instance 0x20bc49f0
I do have an NSNotification
that invokes my getAllItems
:
// Notification received when user returns to app (refreshes the table view)
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(getAllItems) name:UIApplicationWillEnterForegroundNotification object:nil];
This only happens after I switch view controllers using the menu, leave the app, and reopen (the app does not terminate). It does not crash if the user stays in the app, regardless of how many view controllers they switch between.
It's a pretty annoying bug, because although it's very easily reproduce, I don't know how to fix it. Any help is appreciated.