iOS App 的运行周期

App被启动时,从非运行状态到短暂的非激活状态,然后切换到运行状态或者后台运行状态。在启动过程中,操作系统对App创建了一个主线程来调用main方法。

main方法是App的入口,用来调用UIKit框架和做一些程序运行前的预处理。XCode项目模板自动生成了mian方法,调用UIApplicationMain

iOS也有自动内存管理,ARC(Automatic Refenerce Counting),@autoreleasepool中的代码的内存管理被ARC托管

 

UIApplicationMain方法的参数描述

前两个参数 argc 和 argv包含了启动时间,系统传入的所有参数。这些参数得可以被UIKit架构解析,否则的话就被忽略。

第三个参数标识App负责启动App的class的名字,建议给这个参数传递一个nil,这样的话UIKit 就会使用UIApplication这个类。

第四个参数标识自定义的App代理,这个代理负责管理代码和系统之间的高级交互,XCode会自动为这个参数赋值。

 

UIApplicationMain 方法还用来加载App 的主UI文件。

如果App用的是 stroryboards,UIApplicationMain方法会加载storyboard对应的controller并且把controller部署到app的window上。

如果App用的是 nib文件,UIApplicationMain 方法会把 nib文件加载到内存并且部署到app的window上。

必须通过  application:willFinishLaunchingWithOptions: 方法来部署这些文件。

在一个App中 stroyboard 和nib 文件不能共存。

Info.plist文件的 UIMainStoryboardFile 节点配置了主 stroyboard的文件名,NSMainNibFile节点配置了主 nib文件的文件名。

 

 

App在后台运行时,会监听一些后台事件,操作系统仍然加载UI文件,只是不在前端显示。

可以通过 applicationState 属性的值来判断App是在前台运行还是在后台运行,在前台运行时 applicationState 值为UIApplicationStateInactive;后台运行时的值为 UIApplicationStateBackground

 

 

在启动时间需要做的:

当App启动时(进入前台运行时或者后台运行时),需要在 application:willFinishLaunchingWithOptions: 和 application:didFinishLaunchingWithOptions: 这两个方法中做以下处理:

检查App启动选项中的内容,做出合适的响应

初始化App的关键数据结构

准备App需要展示的window

应用OpenGL ES 的 App,绘图环境的初始化应该在applicationDidBecomeActive: 这个方法中做,而不是在上述两个方法中。

 

如果App没有在启动时间自动加载main storyboard 或者nib文件,那么可以在 application:willFinishLaunchingWithOptions: 这个方法中来加载。对于同时支持横屏显示和竖屏显示的App,要始终设置主窗口的根视图控制器在纵向方向。App启动时,如果设备被旋转到不同的方向,那么系统会告诉根视图控制器,让窗口旋转到相应的方向。

 

application:willFinishLaunchingWithOptions: 和 application:didFinishLaunchingWithOptions:  这两个方法应该尽量轻量化,以此来减少程序启动的时间。程序的启动并且初始化不能超过5秒,如果启动时间过长,操作系统会在不做任何通知的前提下 kill 掉这个程序。因此,需要启动一个新线程来处理影响启动时间的事情(例如连接网络)

 

applicationDidBecomeActive:  这个方法会在两个阶段都被调用:程序启动时,和后天状态转换到前台状态时,这个方法需要处理两种状态转换的相同的任务。

后台运行的程序,除了监听事件,其他任何事情都不应该去做。

 

在被中断时需要做的:

让基于alert的中断出现时,比如电话来电,程序会进入 inactive 状态以便于系统来提示用户如何处理,直到用户忽略alert以后,程序才会恢复到前台或者后台状态。

 

 

当中断出现时需要做的处理:

基于Alert的中断会导致App短暂失去控制权,App会继续在前台运行,但是不能从系统接收到任何touch事件。(但是可以接收到通知和其他类型的事件,比如加速度计事件)。为了响应这个变化,应该在applicationWillResignActive: 这个方法中做如下处理:

停止timer和其他定时性的任务

停止所有正在运行的元数据查询

不要初始化新的任务

暂停视频和的播放(AirPlay上播放的例外)

如果App是一个游戏,则把游戏设置成暂停

把OpenGL ES 的帧率设置为0

挂起所有的任务调度队列,或者执行非关键代码的运行队列(不过在inactive的时期,可以继续处理网络请求和其他的比较耗时的任务)

 

当App切换回Active状态时,应在 applicationDidBecomeActive: 这个方法中让App恢复到上述处理之前的状态。

当按下锁屏键时,如果App关联的有被 NSFileProtectionComplete 保护选项保护的文件,那么App应该关掉对所有这些文件的关联。

设置了密码的设备,对于被完全保护的文件,按下锁屏键会强制系统丢掉这些文件的解密秘钥,当屏幕被锁定之后,任何对这些文件的访问都会被禁止。所以如果App引用了这些文件,要在 applicationWillResignActive: 这个方法中取消对这些文件的引用,在applicationDidBecomeActive:方法中对这些文件重新引用。

 

另外一种中断是用户按下锁屏键。当按下锁屏键时,系统会禁用touch 事件,把程序切换到后台并且把程序的 applicationState 的值设置为 UIApplicationStateInactive ,最后锁定屏幕。

在通话过程中切换回App的时候,屏幕顶部的状态条的高度会变大,挂断电话后,状态条的高度又恢复到以前的样子。用view controller来管理view是处理状态条高度变化的最好的方式,当状态条高度变化时,view controller会自动调整view 的高度。如果App没有用到 view controller,那么应该在  UIApplicationDidChangeStatusBarFrameNotification 这个方法中手动对view 的高度做调整,在这个方法中可以获取到状态条的高度。

 

切换到后台

在按下Home键,或者锁屏键,或者操作系统启动了其他App的时候,当前在前台运行的App会被切换到inactive状态,然后切换到后台运行。这些状态切换会导致App调用applicationWillResignActive: 和 applicationDidEnterBackground: 这两个方法。

大多数App在 applicationDidEnterBackground:这个方法返回后,很快就会被挂起。有些App指定了后台任务(比如播放音乐)会向操作系统请求额外的执行时间,这些App会在后台继续运行。

 

    

App切换到后台时,可以调用 applicationDidEnterBackground:方法来做一些准备工作,在切换到后台期间,需要做以下处理:

准备截图  applicationDidEnterBackground:方法返回以后,系统会对App的UI取一个截图,并用这个截图来当做切换的动画。对于包含了敏感信息的View, 那么要在applicationDidEnterBackground: 方法返回之前隐藏或者修改这些View

保存用户数据和App状态  在App进入后台运行的期间,所有被修改过的并且未保存的数据都应该写入到磁盘中保存,这一步骤很有必要,因为App在后台中可能被很多因素被系统给Kill掉。也可以启动一个新线程来做这些保存操作。

尽可能的释放内存  

App的applicationDidEnterBackground:方法只有大约5秒钟的时限来执行完并且返回,如果超出时限,这个App就会被强制Kill掉,并且它占用的内存也会被清理掉。如果有些任务比较耗时,需要更多的时间,那么可以调用 beginBackgroundTaskWithExpirationHandler:方法来请求后台执行时间,然后会把耗时的任务交给新线程。

 

每个进入后台运行的App,都要尽可能的释放内存。系统会的保持足够的内存,保证足够多的App同时运行,当空闲内存不够用的时候,系统会终结掉挂起的App,把资源释放出来。占用内存比较多的App会被操作系统优先终结。所以对于不再用到的对象,App应该丢掉对他们的强引用,这样可以让编译器dispose这些对象,把他们的内存释放出来。但是,如果为了提升性能而缓存了一些对象,那么可以等到App被切换到后台之前,来丢掉这些对象的强引用。

 

在App切换到后台的时候,系统为了减少App对内存的占用,会自动清理掉App中的一些数据的分配

清除核心动画层

清除系统引用的所有缓存图像

清除对一些系统托管的数据缓存的强引用

 

切换到前台

App切换回前台状态时,调用applicationWillEnterForeground: 方法,这个方法用来撤销applicationDidEnterBackground: 中所做的处理,并且要在applicationDidBecomeActive: 方法中继续执行一些程序启动时的任务。

 

程序唤醒时要处理通知队列

被挂起的App不能执行任何代码,而且不能处理任何通知,例如屏幕方向旋转、偏好设置改变或者其他影响App的UI或者状态的通知。为了确保针对这些变化的处理不被丢失,系统会把这些相关的通知加入队列中,然后在App恢复到可执行状态时(前台运行或者后台运行时),系统把队列中的通知分给App去处理。为了避免过多的通知会导致App在恢复过程中超负荷,系统会整合挂起状态中接收到的事件,并且针对每种类型的事件,只对App发送一个通知。

下表列出了可以被整合的事件,和它们对应的的被系统整合之后发送给App的通知。有一些通知会直接传递给App注册的Oberver,另外一些诸如屏幕旋转的通知,会先被系统拦截,然后以另外一种方式传递给App。

 

队列中的通知通常会在App处理任何touch事件和处理任何输入之前传递给App。通常通知队列会在App的恢复过程中被尽快的处理掉。所以,如果App从后台切换回前台过程出现了延时,可以用调试工具来检测是否是因为处理通知的代码导致的。

 

处理iCloud的变化

如果iCloud的状态发生了变化,系统会向App发送一个 NSUbiquityIdentityDidChangeNotification 通知。iCloud的变化包括用户登录/登出,或者启用/禁用iCloud同步。这个通知告知App更新缓存和更新与iCloud相关的UI元素。例如,当用户注销了iCloud,应该删除掉所有基于iCloud的文件和数据。

 

合理的处理语言环境的变化

如果App在挂起的状态中,用户更改了语言环境设置,那么在App回到前台之前,可以利用 NSCurrentLocaleDidChangeNotification  这个通知来更新包含了与语言环境相关的信息(数据,事件,数字格式等)的View。以下是针对语言环境变化的最佳实践:

用 NSLocale 对象的 autoupdatingCurrentLocale 方法,这个方法返回一个 locale 对象,当语言环境发生了变化,这个对象会自动更新。

在语言环境变化时,重新创建所有的日期和数字格式的缓存。

 

响应App的设置的变化

如果在iOS的设置程序中可以管理App的设置,那么用户可能会在App被挂起或者App被切换到后台的时候,更改App的设置。针对这种变化,App应该监听 NSUserDefaultsDidChangeNotification 这个通知。

 

App 被终结

在这些条件下,App没有后台运行和挂起状态,只能直接退出,内存被回收。

App是基于 iOS 4.0 和4.0以前的平台开发的

App被部署到 iOS 4.0 和4.0以前的平台上运行

App所运行的设备不支持多任务

App在Info.plist文件中配置了 UIApplicationExitsOnSuspend 节点

 

App被终结的时候,系统会调用 applicationWillTerminate: ,所以可以在这个方法中做清理工作以及对App状态的保存工作。这个方法只有大约5秒钟的时间执行并返回,如果超过这个时间,App被强制kill。

 

即使在 iOS 4.0 之后的平台,App依然会在没有任何通知的情况下被终结掉,比如用户在多任务的UI中退出App,或者系统会因为内存的限制而强行的清理掉App。App在挂起状态中接收不到终结的通知。只有在后台运行的状态中才可以接收到终结通知,然后调用 applicationWillTerminate: ,在这个方法中不能向系统请求额外的后台执行时间。

posted @ 2014-04-28 19:17  Super猪  阅读(786)  评论(0编辑  收藏  举报