Reputation: 4162
I am writing an Objective-C wrapper for using a GCD timer source. One of the goals is for the wrapper to manage suspending the timer source when the application enters the background.
The docs for -[UIApplicationDelegate applicationDidEnterBackground:]
indicate that it should be used to invalidate timers, among other things; I'm interpreting this somewhat loosely (perhaps that is the source of my problem?) in that I'm suspending a GCD timer (dispatch_suspend()
) in response to UIApplicationDidEnterBackgroundNotification
).
When an instance of the wrapper is initialized, it checks [UIApplication sharedApplication].applicationState != UIApplicationStateBackground
to determine whether the GCD timer source should initially be resumed as part of the initialization sequence. It also registers for UIApplicationDidEnterBackgroundNotification
and UIApplicationWillEnterForegroundNotification
which it uses to suspend and resume the timer source (respectively).
The issue that I have observed is that there seem to be cases where the check of [UIApplication sharedApplication].applicationState != UIApplicationStateBackground
during initialization is passed so that the timer source is resumed, and the next notification that is received is UIApplicationWillEnterForegroundNotification
which causes the timer source to be resumed a second time. This leads to a crash since the timer was not suspended at the time of the second resume.
I can work around this by keeping track of the application state locally and confirming the transitions that would be duplicate, but I am concerned that I may be doing something incorrectly or that there may be a bug (either in implementation or in documentation).
Upvotes: 4
Views: 2102
Reputation: 1540
So here's something you can do to get a better picture of what's going on:
In your apps main.m, add this:
@interface GTTestObject : NSObject
- (void)logNotification:(id)sender;
@end
@implementation GTTestObject
- (void)logNotification:(id)sender {
NSLog(@"%@", [(NSNotification *)sender name]);
}
@end
int main(int argc, char *argv[])
{
@autoreleasepool {
// I'm assuming you'd be using ARC...
GTTestObject *obj = [[GTTestObject alloc] init];
[[NSNotificationCenter defaultCenter] addObserver:obj selector:@selector(logNotification:) name:nil object:nil];
return UIApplicationMain(argc, argv, nil, NSStringFromClass([GTAppDelegate class]));
}
}
Then for every MyAppDelegate.m
method "...didFinishLaunching, ...didEnterBackground, etc." add this:
NSLog(@"[%@ %@]", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
Now you'll be able to see the sequence, time-stamp and (possibly) any conflicts with notification/delegate calls occurring simultaneously.
It's possible you'll get slightly different results from test to test, so run it several times to be sure.
For an example run, I see this every time:
2012-12-21 08:13:58.559 Gamerton[34158:c07] [GTAppDelegate application:didFinishLaunchingWithOptions:] state: UIApplicationStateInactive
2012-12-21 08:13:58.560 Gamerton[34158:c07] UIApplicationDidFinishLaunchingNotification state: UIApplicationStateInactive
...
2012-12-21 08:13:58.561 Gamerton[34158:c07] [GTAppDelegate applicationDidBecomeActive:] state: UIApplicationStateActive
2012-12-21 08:13:58.561 Gamerton[34158:c07] UIApplicationDidBecomeActiveNotification state: UIApplicationStateActive
... Hit home button
2012-12-21 08:16:08.227 Gamerton[34170:c07] [GTAppDelegate applicationWillResignActive:] state: UIApplicationStateActive
2012-12-21 08:16:08.228 Gamerton[34170:c07] UIApplicationWillResignActiveNotification state: UIApplicationStateActive
2012-12-21 08:16:08.229 Gamerton[34170:c07] UIApplicationSuspendedNotification state: UIApplicationStateBackground
2012-12-21 08:16:08.229 Gamerton[34170:c07] [GTAppDelegate applicationDidEnterBackground:] state: UIApplicationStateBackground
... Reopen app
2012-12-21 08:16:59.364 Gamerton[34170:c07] [GTAppDelegate applicationWillEnterForeground:] state: UIApplicationStateBackground
2012-12-21 08:16:59.365 Gamerton[34170:c07] UIApplicationWillEnterForegroundNotification state: UIApplicationStateBackground
2012-12-21 08:16:59.365 Gamerton[34170:c07] _UIApplicationDidRemoveDeactivationReasonNotification
2012-12-21 08:16:59.366 Gamerton[34170:c07] [GTAppDelegate applicationDidBecomeActive:] state: UIApplicationStateActive
2012-12-21 08:16:59.366 Gamerton[34170:c07] UIApplicationDidBecomeActiveNotification state: UIApplicationStateActive
2012-12-21 08:16:59.366 Gamerton[34170:c07] UIApplicationResumedNotification state: UIApplicationStateActive
2012-12-21 08:16:08.230 Gamerton[34170:c07] UIApplicationDidEnterBackgroundNotification state: UIApplicationStateActive
Upvotes: 0
Reputation: 1396
This is how app state works:
On initial Launch,
When you hit the home button,
When you open a backgrounded app,
Opening - (initial launch and opening backgrounded app) - applicationDidBecomeActive gets called - initialize timer here.
Hiding or receiving call, etc - applicationWillResignActive gets called - stop timer here.
Hope this helps.
Upvotes: 2
Reputation: 6806
From the UIApplication docs:
UIApplicationStateInactive
The application is running in the foreground but is not receiving events.
This might happen as a result of an interruption or because the application
is transitioning to or from the background.
So it's possible that your app was in Background, will soon be in Foreground, but is currently transitioning.
Upvotes: 0