iOS-UIApplicationDelegate学习笔记

UIApplicationDelegate协议定义了很多方法,这些方法将会在你的App的生命周期中发生重要的事件时被一个单例对象UIApplication所调用。

该delegate的一些方法使得我们可以在应用状态发生变化时做一些处理操作。比如说,当应用从前台编导后台执行,就会有对应的方法被调用来响应该状态的变化。在很多情况下,这个delegate将是获得这些变化的通知的唯一方法。

根据官方文档描述,app delagate应该扮演一下的关键角色:

  1. 包含应用启动代码;
  2. 响应app的关键变化。明确说就是,应该响应一些暂时的异常和执行状态的改变等事件,比如说你的应用从前台切换到了后台。
  3. 响应别的应用发来的通知,比如说远程通知(推送通知),低内存警告,下载完成通知等等。
  4. 决定是否需要保存应用状态或信息。
  5. 响应app自身的事件。
  6. 可以存储应用的核心数据对象和内容,而不需要拥有任何view controller对象。

应用的启动

一个应用的生命周期中很重要的一部分就是启动时。因为在启动期间,应用会执行很多特定的代码去初始化你的应用。下面是一些在启动阶段会执行的操作:

  • 查看启动选项参数(launchOptions)中的dictionary对象,判断应用是为何为启动。application:willFinishLaunchingWithOptions:application:didFinishLaunchingWithOptions:方法都提供了一个包含启动信息的字典对象,通过查看该对象可以知道应用启动的原因。
  • 决定时都需要存储应用状态。如果应用先前保存过视图控制器的状态的话,那么只需要在application:shouldRestoreApplicationState:方法返回YES的时候再重新存储视图控制器的状态。
  • 注册推送通知服务。为了接受远程推送通知服务,app必须要通过调用registerForRemoteNotificationTypes:方法来注册远程通知服务。
  • 打开一个URL请求。如果有URL需要被访问,系统则会调用application:openURL:options:方法。首先必须要在app的Info.plist文件中添加CFBundleURLType键来声明URL。
  • 给应用提供一个根窗口(root window)对象。从技术上讲,默认情况下Xcode已经自动实现了window的属性赋值,如果你不需要定制自己的应用窗口的话则不需要做任何别的操作。

application:willFinishLaunchingWithOptions:application:didFinishLaunchingWithOptions:方法传递的launchOptions字典参数中包含了很多与app相关的重要启动信息。字典中的信息说明了app启动的原因,从而你有机会更具不同的原因作出启动处理的调整。例如,你的app是因为一个即将到来的远程通知而需要启动,那么你可能就想先配置一些与交互界面相关的数据。

以下就是一些启动原因。

NSString *const UIApplicationLaunchOptionsURLKey;
NSString *const UIApplicationLaunchOptionsSourceApplicationKey;
NSString *const UIApplicationLaunchOptionsRemoteNotificationKey;
NSString *const UIApplicationLaunchOptionsLocalNotificationKey;
NSString *const UIApplicationLaunchOptionsAnnotationKey;
NSString *const UIApplicationLaunchOptionsLocationKey;
NSString *const UIApplicationLaunchOptionsNewsstandDownloadsKey;
NSString *const UIApplicationLaunchOptionsBluetoothCentralsKey;
NSString *const UIApplicationLaunchOptionsBluetoothPeripheralsKey;
NSString *const UIApplicationLaunchOptionsShortcutItemKey;
NSString *const UIApplicationLaunchOptionsUserActivityDictionaryKey;
NSString *const UIApplicationLaunchOptionsUserActivityTypeKey;

状态转换管理

app delegate一个主要的工作就是响应系统通知的应用状态转换事件。对于发生的每个状态转换事件,系统都会调用app delegate中响应的方法。

  • Not running。应用没启动或已终止。
  • Inactive。app在前台运行,但是不接收事件。通常情况app都只是在它转换到别的状态之前在这个阶段短暂停留了一会。
  • Active。app在前台运行,并接收事件。这是一个在前台运行的app的一般状态。一个在激活状态的app不会被其他东西所约束,因为他是前台运行的应用所以它就应该只负责响应用户的操作。
  • Background。app可以执行代码,但是不可见。当用户退出app,系统就会将app短暂的切换到后台状态,然后再进入暂停状态。一个处于后台状态的app应该尽可能少的执行任务。
  • Suspended。app在内存中但不执行代码。系统会暂停那些进入后台状态的app,并且不会再调度它们完成任务任务。并且系统可能会移除暂停状态的app,为其他应用腾空间。

下面这些方法就是在状态转换时会调用的。

  • 启动阶段
    • application:willFinishLaunchingWithOptions:
    • application:didFinishLaunchingWithOptions:
  • 切换到前台
    • applicationDidBecomeActive:
  • 切换到后台
    • applicationDidEnterBackground:
  • 切换到非活动状态
    • applicationWillResignActive: (活动状态切换到非活动状态)
    • applicationWillEnterForeground:(后台状态切换到非活动状态)
  • 终止状态
    • applicationWillTerminate:

下面我们来看一个很简单的小例子。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    NSLog(@"%@", NSStringFromSelector(_cmd));
    return YES;
}

- (void)applicationWillResignActive:(UIApplication *)application {
    NSLog(@"%@", NSStringFromSelector(_cmd));
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
    NSLog(@"%@", NSStringFromSelector(_cmd));
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
    NSLog(@"%@", NSStringFromSelector(_cmd));
}

- (void)applicationDidBecomeActive:(UIApplication *)application {
    NSLog(@"%@", NSStringFromSelector(_cmd));
}

- (void)applicationWillTerminate:(UIApplication *)application {
    NSLog(@"%@", NSStringFromSelector(_cmd));
}

然后我们就会看到这样一个输出过程。其中刚初始化完时时只有前两句输出的。当我们按了home键关闭返回主界面时,就会输出3,4句的结果。当我们通过双击home上滑把应用删除后,按道理来说就会执行applicationWillTerminate:方法。

2016-03-13 14:14:04.926 UIApplicationDelegateDemon[1323:75394] application:didFinishLaunchingWithOptions:
2016-03-13 14:14:04.930 UIApplicationDelegateDemon[1323:75394] applicationDidBecomeActive:
2016-03-13 14:14:09.892 UIApplicationDelegateDemon[1323:75394] applicationWillResignActive:
2016-03-13 14:14:10.482 UIApplicationDelegateDemon[1323:75394] applicationDidEnterBackground:

我们对didFinishLaunchingWithOptions稍作修改。这样就是在判断应用启动的理由,并做一些相应的处理的方式。当然我们这里只是简单判断了三种启动方式而已。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    NSLog(@"%@", NSStringFromSelector(_cmd));

    //说明,如果应用是用户直接启动,则launchOptions内无数据。

    //如果是通过别的应用启动的,则输出对应启动源应用的 Bundle ID
    NSString *bundleId = [launchOptions objectForKey:UIApplicationLaunchOptionsSourceApplicationKey];
    if (bundleId) {
        NSLog(@"%@", bundleId);
    }

    //如果是打开一个URL,则输出需要被打开的URL信息
    NSURL *url = [launchOptions objectForKey:UIApplicationLaunchOptionsURLKey];
    if (url) {
        NSLog(@"%@", url);
    }

    //如果是通过本地通知启动的应用,则输出通知相关信息
    UILocalNotification *localNotification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
    if (localNotification) {
        NSLog(@"Title: %@; Body:%@", [localNotification alertTitle],  [localNotification alertBody]);
    }

    return YES;
}

参考资料

  1. 苹果官方文档