聊一聊iOS的那些生命周期
iOS应用程序的生命周期,还有程序是运行在前台还是后台,应用程序各个状态的变换,这些对于开发者来说都是很重要的。iOS系统的资源是有限的,应用程序在前台和在后台的状态是不一样的。在后台时,程序会受到系统的很多限制,这样可以提高电池的使用和用户体验。
本文所要说到的生命周期,也不仅仅只是应用生命周期;还包括,视图生命周期。
应用生命周期
iOS的应用程序一共有5种状态:
- Not Running(非运行状态)
应用没有运行或被系统终止。
- Inactive(前台非活动状态)
应用正在进入前台状态,但是还不能接受事件处理。
- Active(前台活动状态)
应用进入前台状态,能接受事件处理。
- Background(后台状态)
应用进入后台后,依然能够执行代码。如果有可执行的代码,就会执行代码,如果没有可执行的代码或者将可执行的代码执行完毕,应用会马上进入挂起状态。有的程序经过特殊的请求后可以长期处于Backgroud状态。
- Suspended(挂起状态)
处于挂起的应用进入一种“冷冻”状态,不能执行代码。如果系统内存不够,系统就把挂起的程序清除掉,为前台程序提供更多的内存,应用会被终止。
作为应用程序的委托对象,AppDelegate 类在应用生命周期的不同阶段会回调不同的方法。首先,让我们先了解一下 iOS 应用的不同状态及它们彼此间的关系,如下图所示 :
在应用状态跃迁的过程中,iOS 系统会回调AppDelegate中的一些方法,并且发送一些通知。实际上,在应用的生命周期中用到的方法和通知很多,我们选取了几个主要的方法和通知进行详细介绍,具体如下表所述:
为了便于观察应用程序的运行状态,为AppDelegate.m中的方法添加一些日志输出,具体代码如下:
1 | @implementation AppDelegate |
为了让大家更直观地了解各状态与其相应的方法、通知间的关系,下面以几个应用场景为切入点进行系统的分析。
(一)非运行状态——应用启动场景
场景描述:用户点击应用图标的时候,可能是第一次启动这个应用,也可能是应用终止后再次启动。该场景的状态跃迁过程见下图,共经历两个阶段3个状态:Not running →Inactive→Active。
- 1)在Not running→Inactive 阶段。
调用application:didFinishLaunchingWithOptions:
方法,发出UIApplicationDidFinishLaunchingNotification
通知。
- 2)在Inactive →Active 阶段。
调用 applicationDidBecomeActive:
方法,发出UIApplicationDidBecomeActiveNotification
通知。
(二)点击Home键——应用退出场景
场景描述:应用处于运行状态(即Active状态)时,点击Home键或者有其他的应用导致当前应用中断。该场景的状态跃迁过程可以分成两种情况:可以在后台运行或者挂起,不可以在后台运行或者挂起。根据产品属性文件(如HelloWorld-Info.plist)中的相关属性Application does not run in background 是与否可以控制这两种状态。如果采用文本编辑器打开HelloWorldInfo.plist文件该设置项对应的键是UIApplicationExitsOnSuspend。
状态跃迁的第一种情况:应用可以在后台运行或者挂起,该场景的状态跃迁过程见下图 ,共经历3 个阶段4个状态:Active → Inactive → Background→Suspended。
- 1)在Active→Inactive 阶段。
调用 applicationWillResignActive:
方法,发出UIApplicationWillResignActiveNotification
通知。
- 2)在Inactive →Background阶段。
应用从非活动状态进入到后台(不涉及我们要重点说明的方法和通知)。
- 3)在Background→Suspended 阶段。
调用applicationDidEnterBackground:
方法,发出UIApplicationDidEnterBackgroundNotification
通知。
状态跃迁的第二种情况:应用不可以在后台运行或者挂起,其状态跃迁情况见下图 ,共经历4个阶段5 个状态:Active → Inactive → Background→Suspended→Not running 。
- 1)在Active →Inactivd 阶段。
应用由活动状态转为非活动状态(不涉及我们要重点说明的方法和通知)。
- 2)在Inactive →Background阶段。
应用从非活动状态进入到后台(不涉及我们要重点说明的方法和通知)。
- 3)在Background→Suspended 阶段。
调用applicationDidEnterBackground:
方法, 发出UIApplicationDidEnterBackgroundNotification
通知。
- 4)在Suspended →Not running阶段。
调用applicationWillTerminate:
方法,发出UIApplicationWillTerminateNotification
通知。
iOS 在iOS 4之前不支持多任务,点击Home键时,应用会退出并中断;而在iOS 4之后(包括iOS 4),操作系统能够支持多任务处理,点击Home键应用会进入后台但不会中断(内存不够的情况除外)。
应用在后台也可以进行部分处理工作,处理完成则进入挂起状态。
(三)挂起重新运行场景
场景描述:挂起状态的应用重新运行。该场景的状态跃迁过程如下图所示,共经历3 个阶段4 个状态:Suspended → Background → Inactive → Active 。
- 1)Suspended→Background阶段。
应用从挂起状态进入后台(不涉及我们讲述的这几个方法和通知)。
- 2)Background→Inactive 阶段。
调用applicationWillEnterForeground:
方法,发出UIApplicationWillEnterForegroundNotification
通知。
- 3)Inactive →Active 阶段。
调用applicationDidBecomeActive:
方法,发出UIApplicationDidBecomeActiveNotification
通知。
(四)内存清除——应用终止场景
场景描述:应用在后台处理完成时进入挂起状态(这是一种休眠状态),如果这时发出低内存警告,为了满足其他应用对内存的需要,该应用就会被清除内存从而终止运行,该场景的状态跃迁见下图 。
内存清除的时候应用终止运行。内存清除有两种情况,可能是系统强制清除内存,也可能是由使用者从任务栏中手动清除(即删掉应用)。内存清除后如果应用再次运行,上一次的运行状态不会被保存,相当于应用第一次运行。
在内存清除场景下,应用不会调用任何方法,也不会发出任何通知。
视图生命周期
视图是应用的一个重要组成部分,功能的实现与其息息相关,而视图控制器控制着视图,其重要性在整个应用中不言而喻。
视图生命周期与视图控制器关系
以视图的4 种状态为基础,我们来系统了解一下视图控制器的生命周期。在视图不同的生命周期中,视图控制器会回调不同的方法,具体如下图所示。
在视图控制器已被实例化,视图被加载到内存中时调用viewDidLoad方法,这个时候视图并未出现。在该方法中,通常进行的是对所控制的视图进行初始化处理。
视图可见前后会调用viewWillAppear:
方法和viewDidAppear:
方法;视图不可见前后会调用viewWillDisappear:
方法和viewDidDisappear:
方法。4个方法调用父类相应的方法以实现其功能,编码时该方法的位置可根据实际情况做以调整,参见如下代码:
1 | -(void)viewWillAppear:(BOOL)animated{ |
viewDidLoad
方法在应用运行的时候只调用一次,而这上述4 个方法可以被反复调用多次,它们的使用很广泛但同时也具有很强的技巧性。例如,有的应用会使用重力加速计,重力加速计会不断轮询设备以实时获得设备在z 轴、x 轴和y轴方向的重力加速度。不断的轮询必然会耗费大量电能进而影响电池使用寿命,我们通过利用这4个方法适时地打开或者关闭重力加速计来达到节约电能的目的。怎么使用这4 个方法才能做到“适时”是一个值得思考的问题。
在低内存情况下,iOS 会调用didReceiveMemoryWarning:
和viewDidUnload:
方法。在iOS 6 之后,就不再使用viewDidUnload:
,而仅支持didReceiveMemoryWarning:
。
didReceiveMemoryWarning:
方法的主要职能是释放内存,包括视图控制器中的一些成员变量和视图的释放。现举例如下:
1 | - (void)didReceiveMemoryWarning { |
除了上述5 个方法视图控制器外,还有很多其他方法。
iOS UI 状态保持和恢复
iOS 设计规范中要求,当应用退出的时候(包括被终止运行的时候),需要保持界面中UI元素的状态,当再次进来的时候看到的状态与退出时是一样的。在iOS 之后,苹果提供以下API使得UI状态保持和恢复变得很容易。
在iOS 中,我们可以在以下3种地方实现状态保持和恢复:
应用程序委托对象
视图控制器
自定义视图
恢复标识是iOS为了实现UI状态保持和恢复添加的设置项目。我们还需要在应用程序委托对象AppDelegate代码部分做一些修改,添加的代码如下:
1 | -(BOOL) application:(UIApplication *)application shouldSaveApplicationState:(NSCoder *)coder{ |
其中application:shouldSaveApplicationState:方法在应用退出时调用,负责控制是否允许保存状态,返回YES 情况是可以保存,NO是不保存。
application:shouldRestoreApplicationState:
方法在应用启动时调用,负责控制是否恢复上次退出时的状态,返回YES 表示可以恢复,返回NO表示不可以恢复。
application:willEncodeRestorableStateWithCoder:
方法在保存时调用,在这个方法中实现UI状态或数据的保存,其中[coder encodeFloat:2.0 forKey:@"Version"]
语句是保存简单数据。
application:didDecodeRestorableStateWithCoder:
方法在恢复时调用,在这个方法中实现UI状态或数据的恢复,其中[coder decodeFloatForKey:@"Version"]
语句用于恢复上次保存的数据。
想要实现具体界面中控件的保持和恢复,还需要在它的视图控制器中添加一些代码。我们在ViewController.m中添加的代码如下:
1 | -(void)encodeRestorableStateWithCoder:(NSCoder *)coder{ |
在iOS 6之后,视图控制器都添加了两个方法——encodeRestorableStateWithCoder:
和decodeRestorableStateWithCoder:
,用来实现该控制器中的控件或数据的保存和恢复。
其中encodeRestorableStateWithCoder:
方法在保存时候调用,[coder encodeObject:self. txtField.textforKey:kSaveKey]
语句是按照指定的键保存文本框的内容。
decodeRestorableStateWithCoder:
方法在恢复时调用,[coder decodeObjectForKey:kSaveKey]
在恢复文本框内容时调用,保存和恢复事实上就是向一个归档文件中编码和解码的过程。
移除Main.storyboard
每次使用Single View Application模板创建工程之后,总是会有一个Main.storyboard文件,那么,当我们使用代码布局的时候,很显然是不需要它的。那么,如何将它从工程中移除呢?只要进行如下几步即可。
在工程配置中移除关联
在TARGETS中,将Main InInterface选项中的值清空并保存设置。
移除Main.storyboard中的关联文件
选择storyboard文件。将类关联文件项清空并保存设置。
移除Main.storyboard文件
从工程中移除文件。
在AppDelegate中添加代码
1 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { |
完成以上几步,运行工程即可,顺利运行,没有出现任何error或waring。