一、iOS应用程序状态机一共有五种状态:

1. Not running:应用还没有启动,或者应用正在运行但是途中被系统停止。

2. Inactive:当前应用正在前台运行,但是并不接收事件(当前或许正在执行其它代码)。一般每当应用要从一个状态切换到另一个不同的状态时,中途过渡会短暂停留在此状态。唯一在此状态停留时间比较长的情况是:当用户锁屏时,或者系统提示用户去响应某些(诸如电话来电、有未读短信等)事件的时候。

3. Active:当前应用正在前台运行,并且接收事件。这是应用正在前台运行时所处的正常状态。

4. Background:应用处在后台,并且还在执行代码。大多数将要进入Suspended状态的应用,会先短暂进入此状态。然而,对于请求需要额外的执行时间的应用,会在此状态保持更长一段时间。另外,如果一个应用要求启动时直接进入后台运行,这样的应用会直接从Not running状态进入Background状态,中途不会经过Inactive状态。比如没有界面的应用。注此处并不特指没有界面的应用,其实也可以是有界面的应用,只是如果要直接进入background状态的话,该应用界面不会被显示。

5. Suspended:应用处在后台,并且已停止执行代码。系统自动的将应用移入此状态,且在此举之前不会对应用做任何通知。当处在此状态时,应用依然驻留内存但不执行任何程序代码。当系统发生低内存告警时,系统将会将处于Suspended状态的应用清除出内存以为正在前台运行的应用提供足够的内存。

如下图:

注意:运行在iOS3.2或更早期版本操作系统之上的应用并不进入后background和suspended状态。另外,一些即使运行在iOS4或更新版本操作系统但是不支持多任务或后台执行的应用,也不会进入background和suspended状态。相应的这些应用在从前台运行状态离开时会直接被终止。

大多时候状态转换通过调用你的应用委托对象继承的Delegate方法来完成。开发人员可以在提供的这些继承方法中做任何事,以响应状态转换。相关继承的方法及介绍如下所示:

各个程序运行状态时代理的回调:

状态1:- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions
      告诉代理进程启动但还没进入状态保存

状态2:- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
     告诉代理启动基本完成程序准备开始运行

状态3:- (void)applicationWillResignActive:(UIApplication *)application
    当应用程序将要入非活动状态执行,在此期间,应用程序不接收消息或事件,比如来电话了

状态4:- (void)applicationDidEnterBackground:(UIApplication *)application
    当程序被推送到后台的时候调用。所以要设置后台继续运行,则在这个函数里面设置即可

状态5:- (void)applicationWillEnterForeground:(UIApplication *)application
    当程序从后台将要重新回到前台时候调用,这个刚好跟上面的那个方法相反。

状态6:- (void)applicationDidBecomeActive:(UIApplication *)application 
     当应用程序入活动状态执行,这个刚好跟上面那个方法相反

状态7:- (void)applicationWillTerminate:(UIApplication *)application
    当程序将要退出是被调用,通常是用来保存数据和一些退出前的清理工作。这个需要要设置UIApplicationExitsOnSuspend的键值。

- (void)applicationDidFinishLaunching:(UIApplication*)application--------3.0后已启用...
    当程序载入后执行

如下的事件:

事件1. 当程序启动的时候,状态的变化

开始--->状态1----->状态2---->状态6

事件2. 程序在前台的时候,按下Home键,状态的变化

状态6--->状态3----->状态4

事件3. 程序在前台的时候,按下锁屏键

同事件2:状态6--->状态3----->状态4

事件4. 在事件3结束后,解锁

状态4--->状态5----->状态6

事件5. 程序在前台的时候,自动锁屏了

同事件2:状态6--->状态3----->状态4

事件6. 状态7的验证现在还没有好的验证方法,难道在使用的过程中意外的崩溃了,现在还没有研究清楚

在上面8个方法对应的方法中键入NSLog打印。

现在启动程序看看执行的顺序:

启动程序
lifeCycle[40428:11303] willFinishLaunchingWithOptions
lifeCycle[40428:11303] didFinishLaunchingWithOptions
lifeCycle[40428:11303] applicationDidBecomeActive

按下home键

lifeCycle[40428:11303] applicationWillResignActive
lifeCycle[40428:11303] applicationDidEnterBackground

双击home键,再打开程序

lifeCycle[40428:11303] applicationWillEnterForeground
lifeCycle[40428:11303] applicationDidBecomeActive

二、关于main函数,UIApplication类和UIApplication代理类

每一个iPhone程序都包含一个UIApplication对象,它管理整个程序的生命周期,从加载第一个显示界面开始,并且监听系统事件、程序事件调度整个程序的执行。

UIApplication的核心作用是提供了iOS程序运行期间的控制和协作工作。

iPhone应用程序是由主函数main启动,它负责调用UIApplicationMain函数,该函数的形式如下所示: 
int UIApplicationMain ( 
int argc, 
char *argv[], 
NSString *principalClassName, 
NSString *delegateClassName 
);

在main函数中第二行代码UI Application Main(argc, argv, nil, nil);对UIApplication对象进行了初始化,这个对象是隐含的,这个方法除了argc 和 argv 参数外,另外这个函数还有两个字符串参数来识别UI Application类和UI Application代理类,在这里默认是2个nil,第一个参数为nil就默认把UI Application类作为缺省值进行初始化,可以在这里不填nil而是使用自己定义的UI Application子类。至于第二个参数nil,这里有了UI Application对象怎么又出来一个UI Application代理类对象呢?这里需要说明UI Application对象说是管理整个程序的生命周期其实它是什么具体的事情都不干,它只负责监听事件当需要做实际工作的时候就交给UI Application代理类去做,UI Application相当于传令官负责只把命令传达给UI Application代理类这个士兵,然后由这个士兵真正去冲锋陷阵,所以需要给UI Application对象设置代理类。
那么UIApplicationMain函数到底做了哪些事情呢?这个函数主要负责三件 事情:

1)从给定的类名初始化应用程序对象,也就是初始化UIApplication或者子类对象的一个实例,如果你在这里给定的是nil,那么 系统会默认UIApplication类,也就主要是这个类来控制以及协调应用程序的运行。在后续的工作中,你可以用静态方法sharedApplication 来获取应用程序的句柄。

2)从给定的应用程序委托类,初始化一个应用程序委托。并把该委托设置为应用程序的委托,这里就有如果传入参数为nil,会调用函数访问 Info.plist文件来寻找主nib文件,获取应用程序委托。

3)启动主事件循环,并开始接收事件。

上面是UIApplicationMain函数的工作,接下来一个问题是应用程序视图的显示、消息的控制怎么办?下面就是UIApplication(或 者子类)对象的职责,这个对象主要做下面几件事:

1)负责处理到来的用户事件,并分发事件消息到应该处理该消息的目标对象(sender,  action)。 
2)管理以及控制视图,包括呈现、控制行为、当前显示视图等。 
3)该对象有一个应用程序委托对象,当一些生命周期内重要事件(可以包括系统事件或者生命周期控制事件)发生时,应用程序通知该对象。例如,应用程序启 动、内存不够了或者应用程序结束等,让这些事件发生时,应用程序委托去响应。

通 过上面的分析,可以知道UIApplication对开发者来说,是一个黑箱,因为所有的操作,都可以由它的委托来帮我们完成,它只需要在 后面维护一些不可更改的东西,如事件消息分发和传递、给委托发送事件处理请求等等,如,应用程序加载处理完毕,它会发送消息给委托,然后委托可以在 applicationDidFinishLanching委托函数中去实现开发者想要的动作。利用XCODE在创建应用程序时,会默认实现一个应用程序 委托类。而对于加载的视图,则有视图相关的委托类来处理视图加载过程的生命事件。下面说明委托主要可以办哪些事情: 
控制应用程序的行为

- (void)applicationDidFinishLaunching:(UIApplication *)application 
          应用程序启动完毕。 
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
        当由于其它方法打开应用程序(如URL指定或者连接),通知委托启动完毕 
- (void)applicationWillTerminate:(UIApplication *)application 
         通知委托,应用程序将在关闭 退出,请做一些清理工作。 
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application 
        通知委托,应用程序收到了为来自系统的内存不足警告。-(void)applicationSignificantTimeChange:(UIApplication *)application 
      通知委托系统时间发生改变(主要是指时间属性,而不是具体的时间值) 
打开URL 
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url 
           打开指定的URL 
控制状态栏方位变化 
– application:willChangeStatusBarOrientation:duration: 
        设备方向将要发生改变 
– application:didChangeStatusBarOrientation: 
活动状态改变 
- (void)applicationWillResignActive:(UIApplication *)application 
   通知委托应用程序将进入非活动状态,在此期间,应用程序不接收消息或事件。-(void)applicationDidBecomeActive:(UIApplication *)application 
      通知委托应用程序进入活动状态,请恢复数据

可以看到UIApplication的头文件实现 
@interface UIApplication :UIResponder { 
@package 
id _delegate ;  //这就是应用程序委托。 
NSTimer ....... 

因此,在UIApplication中处理的系统事件时,只需转到_delegate这个类去处理, 这个类对象就是应用程序委托对象。我们可以从应用程序的单例类对象中得到应用程序委托的对象 
UIApplicationDelegate* myDelegate = [[UIApplication sharedApplication] delegate];

UIApplication 接收到所有的系统事件和生命周期事件时,都会把事件传递给UIApplicationDelegate进行处理,对于用户输入事件,则传递给相应的目标对象去处理。比如我们在应用程序被来电等消息后,可以调用应用程序委托类的 applicationWillResignActive()方法,这个方法在用户锁住屏幕时,也会调用,与之相适应的是应用程序重新被用户打开时的委托 方法。另外常用的就是内存不足的系统警告,此时会调用应用程序委托类的applicationDidReceiveMemoryWarning()方法, 然后我们就可以试着释放一些内存了。

三、 iOS应用状态切换:

1. 应用启动周期

当应用启动时,将从Not running状态进入foreground或者直接进入background运行。进入前台时,其实最终是要进到Active状态,中途会先短暂进入到Inactive状态。在应用启动时,系统会创建一个process和一个主thread,并且在主thread中调用main函数,即上面二提到的。 main函数在创建工程时由Xcode自动生成。main函数负责UIApplication对象初始化,及设置UIApplication代理类等等。在应用初始化并准备进到前台运行之前的大部分工作都在main函数中完成。

应用被启动直到前台运行的过程如下图,右侧部分为调用的UIApplication代理类的方法。

如果你的应用要求启动后直接进入到Backgroundu状态,则对应的启动过程和上稍有区别,主要不同是,上面的应用是进入到active,而你的应用要进入到background,并且处理事件,当没有事件处理时,会被挂起进入到Suspended状态。如下图所示。

需要注意的一点是:对于从Not running状态直接进入到background状态的应用,在启动进入到background状态时,如果应用有界面,系统仍然会加载用户界面文件,只是不会显示在应用的window上面。
    为了在程序中确定你的程序是进入到了foreground还是background,你可以在application:didFinishLaunchingWithOptions:   方法中检测UIApplication类对象的applicationState属性,如果应用进入到了foreground,则属性值为UIApplicationStateInactive,如果进入到了background,则为UIApplicationStateBackground。

检测示例代码:

UIApplicationState state = [UIApplication sharedApplication].applicationState;
    return (state==UIApplicationStateActive || state==UIApplicationStateInactive );

注:当应用启动时要求打开一个URL,则此类应用的启动过程和三中的两个图又有稍微区别, 具体如下:

具有自定义URL模式的应用必须能够处理所有传递给它的URLs。所有的URL都是传递给应用的代理来处理,无论当前应用是处在启动阶段或是正在运行running或是在后台background。为了能够处理URL请求,你的应用代理必须实现下面的接口方法:

(1)使用application:didFinishingLaunchingWithOptions:方法检索URL信息,并且决定是否想要打开这个URL,这个方法只有在应用被启动的时候调用。

(2)iOS4.2或更新的版本,使用方法application:openURL:sourceApplication:annotation:方法去打开文件。

(3)iOS4.1或更老的版本,使用方法application:handleOpenURL:方法去打开文件。

当URL请求到达时,如果你的应用没在正在运行,则会被启动并且移到前台运行以打开URL。你的application:didFinishingLaunchingWithOptions:方法实现中应该包含从选项字典options dictionary中检索URL并且判断该应用能否打开它的部分。如果能够打开,则返回YES,让方法application:openURL:sourceApplication:annotation:或方法application:handleOpenURL:去处理具体的URL打开过程。对于要求启动时打开URL的应用,启动顺序如下图所示:

当URL请求到来时,如果你的应用正在background运行或被suspended,它将会被移到前台以打开URL。之后不久,系统将会调用应用代理的application:openURL:sourceApplication:annotation:方法去检测URL并打开它。如果你的应用代理没有实现这个方法(或者当前系统是iOS4.1或更老的版本),系统将会调用应用代理的application:handleOpenURL:方法来代替。下面是唤醒后台或挂起的应用,去打开URL的程序执行流程,如下图所示:

支持自定义URL模式的应用,可以在应用启动和去处理URL之前,这个过程之间指定不同的启动画面图像。具体细节,请看Apple官方文档iPhoneAppProgrammingGuide.pdf第85页,“Providing Launch Images for Custom URL Schemes”。

2. 响应中断

当一个基于警告的中断(诸如电话来电)发生时,应用会暂时从active状态切换到Inactive状态,以给系统提供机会提示用户,让用户决定如何处理。在用户决定如何处理此中断警告之前,应用将一直处于Inactive状态。 在用户做出选择后,当前应用或者回到active状态继续运行,或者直接切换到background状态以让位于其它的应用运行。此种情况下,应用执行流程如下图所示:

在iOS5中,notification,特指显示banner方式的notification,并不会像上面的中断一样使当前处于active状态的应用切换到Inactive状态。此类通知的banner放置在你的应用窗口的上边沿之上,所以你的应用依然处在active状态,并且继续像以前一样接收touch events。但是,如果用户拉下banner去呈现通知中心内容时,当前应用将会和上面基于警告的中断一样切换到inactive状态。此时应用将一直处于Inactive状态直到用户对拉下的banner通知做出处理,或许仅仅清除通知或者启动另外一个应用。相应的当前应用要么切换回active状态继续运行或者切换到background状态。用户可以通过Settings应用来配置哪些Notifications以banner的形式显示,哪些以alert警告的形式显示。

用户按“休眠/唤醒”键是另外一种类型的中断,这类中断促使应用被deactived,当用户按下“休眠/唤醒”键时,系统除能触摸事件,deactivate当前的应用,并且锁屏。针对使用数据保护进行加密文件的应用,锁屏事件除了上面的deactivated应用,除能触控事件之外还有其它的处理过程。

当中断发生时,会做什么?

对于基于警告的中断将会导致用户暂时对应用失去控制。当前应用继续在前台foreground运行,但是不再接收任何触控事件。(事实上,应用只是不再接收触控类事件,其它类型的事件比如accelerometer事件,和通知Notification,应用仍然接收。)所以为了响应这些变化,应用需要在applicationWillResignActive:方法中做以下工作:

(1)停止timers及终止其它周期性任务。

(2)停止任何正在运行的元数据查询。

(3)不再初始化任何新任务。

(4)暂停电影播放(在AirPlay上的播放除外)

(5)游戏进入暂停状态。

(6)恢复OpenGL ES帧率。

(7)暂停任何正在临界区执行的分发队列或操作队列。(当然,当应用处于inactive状态时,应用仍然可以继续处理网络请求以及其它一些对时间敏感的后台任务)

当应用恢复切换回active状态时,将会在applicationDidBecomeActive:方法中恢复应用被挂起时执行applicationWillResignActive:方法中所做的所有工作。因此,当应用重新被激活reactivate时,应用应该重启timers,恢复任何分发队列,以及恢复OpenGL ES帧率。但是,游戏不应该自动恢复运行,应该继续保持在暂停状态直到用户手动恢复它们。

当用户按下“休眠/唤醒” 键时,带有NSFileProtectionComplete保护选项需要对文件进行保护的应用必须关闭所有对文件的引用。对于带有密码的设备,按下“休眠/唤醒”键时,锁屏,并且强制系统扔掉解密密钥,以使完全保护使能。当屏被锁时,任何尝试访问相应受保护文件的操作都将fail。所以如果你的应用中有此类受保护的文件时,你应该在applicationWillResignActive:方法中关闭所有对这些文件的引用,并且在applicationDidBecomeActive:方法中重新打开对此类文件的引用。

在通话过程中,调整你的应用的UI:

当用户正在接电话,并且返回你的应用继续保持通话,此时状态栏的高度应该增加以反应用户正在通话的事实。相似的,当用户结束通话时,状态栏的高度应该缩减恢复常规高度。处理状态栏高度变化的最好方法是使用view controllers去管理你的应用views。当状态栏frame size改变时,view controllers会自动调整它们所管理的所有内部视图。

如果你的应用因为某些原因而没有使用view controllers,则你应该手动响应状态栏frame size的变化,具体即通过注册UIApplicationDidChangeStatusBarFrameNotification通知来实现。通知处理函数handler应该获取状态栏的高度并且使用这些数据来适度调整当前应用所包含视图的高度。

3. 切向后台background状态

当用户按下"Home"键或者系统启动另外一个应用时,前台foreground应用首先切换到Inactive状态,然后切换到Background状态。此转换将会导致先后调用应用代理的applicationWillResignActive:和applicationDidEnterBackground:方法。在applicationDidEnterBackground:方法返回后,大部分应用在之后不久转入suspended状态。对于请求特定后台background任务的应用,比如播放音乐应用,或者那些请求需要额外执行时间的应用,可能会继续执行更长一段时间。具体流程如下图所示:

注:应用从froeground切换到background只有在支持多任务并且运行iOS4.0或更新版本系统的设备上才会发生。所有其它的情况,应用不是切向后台,而是直接终止,并且从内存中清除。

应用切向后台background时应该做什么:

应用可以在applicationDidEnterBackground:方法中做些切向background状态前需要做的一些准备工作,当切向background状态时,所有的应用需要做以下事情:

(1)应用界面快照。当applicationDidEnterBackground:方法返回时,系统保存应用界面的快照,并且使用快照图片作为转换动画。如果在你的应用界面中有涉及到敏感信息的视图,则你应该在applicationDidEnterBackground:方法返回前隐藏或者修改这些视图。

(2)保存用户数据和应用状态信息。所有没有保存的改变都应该在切向background状态前写入磁盘以保存。这一步是必须的,因为你的应用在后台时很有可能因为多种其它原因而被很快kill掉。根据需要你可以在background thread后台线程中执行这些操作。

(3)释放尽可能多的内存资源。

applicationDidEnterBackground:方法允许最多有5秒的时间去完成任何任务然后返回。实际中,此方法应该尽可能快的返回。如果在时间到期之后,此方法没有返回,则应用即被kill掉,并且清除所占用的内存。如果你的应用确实需要更多的时间去执行任务,可以调用beginBackgroundTaskWithExpirationHandler:方法请求后台执行时间,然后启动一个能长期执行任务的线程。无论你是否启动一个执行后台任务的线程,applicationDidEnterBackground:方法都必须在5秒后退出。

注:UIApplicationDidEnterBackgroundNotification通知也会发送,以让应用对此通知感兴趣的部分知道当前应用正切向background状态。你的应用中的对象可以使用默认的通知中心注册这个通知。

依据不同的应用场合,应用切向后台时还有很多其它的事情需要做,比如active状态的Bonjour服务应该暂停,应用应该停止调用OpenGL ES函数。

因为前台应用在使用系统资源和硬件时一直比后台应用具有更高的优先权。运行在后台的应用应该对此差异有心理准备,并且在后台运行时要调整它们的访问资源行为。特别的,当应用切向background时尤其要遵循以下几点:

(1)不要在应用代码中调用任何OpenGL ES的东西。当应用在后台运行时不可以创建EAGLContext对象或者发出任何OpenGL ES绘画命令,使用这些调用将会导致应用立即被kill掉。应用也必须保证先前提交发出的所有命令在应用切向background状态前都已执行完毕。具体细节请参考“OpenGL ES Programming Guide for iOS”中“Implementing a Multitasking-aware OpenGL ES Application”部分。

(2)在应用挂起suspended之前取消所有Bonjour相关的服务。当应用转向后台,并且在被挂起前,应用应该unregister Bonjour服务并且关掉任何和网络服务相关的sockets监听。挂起的应用是没法响应这些服务请求的。如果你的应用不关掉这些和Bonjour相关的服务,当应用被挂起的时候,系统会自动帮你关掉这些服务。

(3)在基于网络sockets的应用中,需要处理连接失败的情况。当你的应用因为某些原因而被挂起时,系统可能会拆除socket连接。只要你的应用对尽可能多的网络错误情况都有很好的处理,像丢掉信号等,此类问题不会导致你的应用出现不正常。当应用从后台退出恢复执行时,如果遇到sockets使用错误,简单的重建socket连接即可。

(4)在切向background状态前保存应用状态。在低内存告警时,后台应用可能会被清除出内存以释放空间。处于suspended状态的应用被优先清除内存,并且在被清除前不会给出任何通知。因此,当应用切入background状态前一定要保存足够多的应用状态信息以便后面恢复时使用。

(5)当切向后台时,释放所有不再需要的内存。如果你的应用保持着一个很大的内存缓存对象(比如图像),则切入后台前,释放所有的对这些缓存对象的引用。

(6)在被挂起前停止使用系统共享资源。使用系统共享资源(比如Address Book或Calendar Data)的应用,在被挂起前必须停止对这些共享资源的使用。对这些资源的使用,前台应用具有更高的优先使用权,如果发现你的应用在被挂起后还没有停止对这些共享资源的使用,则应该将被kill掉。

(7)避免更新应用窗口和视图。当应用处在后台时,应用窗口和视图是不可见的,所以不需要更新它他。尽管在后台创建和操纵窗口和视图对象并不会导致应用被kill掉,但是可以考虑将这些工作推迟到应用返回前台时执行。

(8)响应外部附件连接和失去连接通知。针对和外部附件有通信的应用,当应用切向background状态时,系统会发送一个disconnection通知。应用必须注册此通知并且使用它去关掉当前的附件访问session。当应用返回foreground时,会有一个与之匹配的通知被发送,给应用提供重新建立session的机会。

(9)切向后台时,清除行为警告相关的资源。为了在应用相互切换之间保存应用上下文,当应用切向后台时,系统并不自动dismiss action sheets(UIActionSheet)和alert views(UIAlertView)。由应用设计者去提供具本的清除方案。对于运行在iOS4.0版本之前的应用,在退出时action sheets和alerts仍然被dismiss掉,以让应用的取消处理函数有机会去运行。

(10)切向后台时,移除所有敏感视图信息。因为系统会快照应用界面并且生成应用切换动画,所以带有敏感信息的视图或窗口必须隐藏或移除,具体原因前面已介绍。

(11)应用在后台运行时执行最少量化的工作。系统给后台运行的应用的执行时间和给前台运行的应用相比,通常非常有限。如果应用在后台播放音频或者监测位置变化,则应用应该仅关注此任务,所有不必要的任务都应该被推迟。在后台执行时间过长的应用会被系统throttled back或者直接被kill掉。

当应用因为系统内存告警需要被清除出内存时,应用会调用他的代理的applicationWillTerminate:方法去执行应用退出前的最后的任务。

后台应用的内存使用:

当应用切入background时,每个应用应该释放尽可能多的实际占用的内存。系统尽量尝试在内存中同时保持尽量多的应用,但是当内存即将耗尽时,系统会终止那些挂起suspended的应用以回收内存。然而那些消耗很大数量的内存同时又处于后台background运行的应用会优先被终止。

实事求是的讲,就是当你的应用在不再需要的时候要尽快的移除对那些用过对象的引用。移除引用允许自动引用计数系统去释放对象并且回收内存。然而,如果应用为了改进性能而使用了缓存,则应用应该在切换至后台状态前等待并且释放这些缓存。下面是一些需要回收的对象的例子:

(1)缓存的图像对象

(2)比较大的多媒体文件或数据文件,这些文件可以从磁盘重新装载。

(3)任何应用当前不再需要的对象,并且这些对象后面又可以很容易重新创建。

为了帮助您的应用程序,减少其内存占用,系统会自动释放出许多幕后用于支持您的应用程序的对象。例如:

(1)释放所有的核心动画层的后备存储,以避免这些层继续在屏幕上显示,同时又不改变当前层的属性。并且并不释放层对象自已。

(2)移除所有对缓存图像的引用。(如果应用没有对这些图像强引用,则他们随后即被移除内存)

(3)释放一些系统管理的其它的数据缓存。

4. 返回前台foreground

如果应用曾被移入后台,相应的任务被停止,则此时返回前台时可以重启任务继续执行。应用的applicationWillEnterForeground:方法应该恢复所有在applicationDidEnterBackground:方法所做的工作。同时,applicationDidBecomeActive:方法应该继续执行在应用启动时所做的同样的激活任务的操作。应用从后台切入前台的程序流程如下图所示:

注:如果应用在默认的通知中心注册了UIApplicationWillEnterForegroudNotification通知,则当应用重新进入前台时,该通知也是可用的。

(1)在应用切向前台被唤醒时处理通知队列

被挂起的应用要时刻准备当恢复foreground或background状态时去处理所有的通知队列。因为应用被挂起时不能执行任何代码,因此没有办法处理那些和诸如方向改变、时间改变、偏好改变、以及其它的影响应用的外观的行为或状态等等。为了保证这些改变不丢失,系统将这些相关的通知入队列,并且当应用恢复foreground或background重新执行代码时,立即将这些通知发往应用。为了防止应用恢复时因为通知过多而过载,系统会合并事件并且仅传递一个能够反应自从应用被挂起有网络改变的单个通知。

具体通知合并规则如下表所示:

大部分通知直接传递给注册它作的observers,然而像方向改变这样的通知很明显是被系统框架解析的,这样的通知以另外的方式传递给应用。

通知队列典型的在任何触控事件和用户输入之前被投递向应用的主运行循环main run loop。大多数应用应该能够足够快的处理这些事件以致于不会造成应用恢复时有任何明显的滞后。然而,如果发现你的应用在从后台恢复时看起来明显呆滞,则可以使用Instruments去确定是否是通知处理代码正在运行而造成了延迟。
一个应用在返回前台时也会接收所有自从上更新后被标记为dirty的视图的更新通知。处于后台background运行的应用也可以调用setNeedsDisplay或setNeedsDisplayInRect:方法去请求视图更新。然而,因为这时界面不可见,所以系统合并这些请求并且只有当应用恢复前台后才去更新视图。

(2)从容的处理本地改变

当应用处于挂起suspended状态时,如果用户改变了当前语言设置,则当应用返回前台的时候可以使用NSCurrentLocalDidChaneNotification通知来强制任何包含本地敏感信息(像日期、时间、数字等等)的视图进行更新。当然,避免本地信息相关的事件处理的最好的方法还是以那种更容易更新视图的方式来写更新视图的代码。比如:

a.使用autoupdatingCurrentLocal类方法来检索NSLocal对象。此方法返回一个本地对象,该对象响应本地改变并且自动更新自已。所以,你不需要再去重新创建它。然后,当本地信息改变时,你的应用仍然需要去刷新那些包含本地信息的视图。

b.无论任何时候本地信息改变时都去重新创建缓存日期或者数字格式对象。

(3)响应应用设置改变

如果应用包含被Settings应用所管理的设置,则应用应该关注NSUserDefaultsDidChangeNotification通知。因为当你的应用处于后台或被挂起状态时,用户可以修改设置,所以你的应用中可以使用这个通知来响应任何重要的设置改变。某些情况下,响应此通知可以帮助关掉一些潜在的安全漏洞。例如,email程序应该响应用户帐户信息的改变,如果不能成功的监测这些改变将会造成一些隐私和安全方面的问题。比如,用户很有可能发送邮件时还是使用的是老用户帐户,即使那个帐户已经不再属于该用户,然而用户确丝毫不情以为用的就是新帐户。当应用接收到该通知时,应用应该重新加载所有和设置相关的东西并且适当的复位用户接口,如果必要的话。比如密码或其它的安全相关的信息改变时,应用应该隐藏任何先前显示的信息并且强制用户输入新密码。

5.应用终止

尽管应用通常被切向后台或被挂起,但是如果有任何下面的情况发生时,应用将被终止并且清除出内存:

(1)应用依赖于 iOS4.0以前的版本OS

(2)应用部署在运行iOS4.0版本操作系统的设备上

(3)当前设备不支持多任务

(4)应用在Info.plist文件中包含UIApplicationExitOnSuspend key。

如果应用将被终止时正在前台或后台运行,系统将会调用应用代理的applicationWillTerminate:方法以使应用能做退出前的任何需要的回收处理。你可以使用此方法保存用户数据或应用状态信息,以供应用随后重新启动恢复状态时使用。该方法最长运行时限为5秒,过期应用即被kill掉并且移除内存。

注:应用当前被suspended时,不会调用 applicationWillTerminate:方法。

即使是使用iOS SDK4或更新的版本SDK开发应用,也应该考虑应用在没有任何通知时被kill掉的情况。用户可以使用多任务UI很明确的kill掉某个应用。除此之外,如果发生内存告警,系统也会从内存中移除应用以释放空间。处于suspended状态的应用被终止时不会有任何通知。但是如果应用当前正在后台background运行,则当应用要被终止时,系统会调用应用代理的applicationWillTerminate:方法。应用不可以在此方法中申请额外的后台执行时间。

6.主运行循环main run loop

应用主运行循环负责处理所有用户相关的事件。UIApplication对象在应用启动时安装主运行循环并且使用此循环去处理事件和处理基于视图的界面更新。正如名字所表明的,该主运行循环是在应用的主线程app's main thread中运行的。以此保证所有用户事件是按照它们被接收时的顺序串行的执行。

下图展示了主运行循环的结构以及用户事件如何导致了应用行为。当用户和应用交互时,和这些交互相关的事件由系统自动产生并且借助UIKit设定的特殊端口传递给应用。事件在应用内部以队列的形式存在并且一个一个的被分发到应用的主运行循环去执行。UIApplication对象是第一个接收事件的对象,并且决定需要如何处理事件。触控事件通常被分发到应用的主窗口对象,并且最终分发到发生该触控事件的视图上面。其它的事件传递也许会经过各种各样的应用对象而与触控事件传递稍微有所不同。

在iOS应用中可以传递很多类型的事件。最常见的事件列在下表中:

这些事件类型中的大部分通过应用的主运行循环进行传递,但是还有一些并不是的。例如:accelerometer事件直接被传递到应用指定的accelerometer代理对像。关于系统如何处理大多数类型事件,包括touch、remote control、motion、accelerometer,以及gyroscopic事件,详见Event Handling Guide for iOS.

一些像触控、远程控制类的事件,通常被应用的响应对象处理。响应对象存在于应用的任何地方。(UIApplication对象,view对象,view controller对象等等都是响应对象的例子)。大多数事件是以特定的响应对象为目标,但是也可以被传递给其它的响应对象(借助响应链),例如:一个不处理任何事件的view可以将事件传递给它的父view或传递给view controller。

发生在controls类的视图(例如button)上的事件的处理过程和发生在其它类型的views上的触控事件处理过程有些不一样。因为发生在control类的对象上面的交互行为只有非常有限的几种,因此这些交互重新打包进active message并且传递给合适的目标对象。  这种target-action的设计模式,使应用通过control类型的view对象去触发一段自定义代码的执行变得非常容易。

iOS应用程序状态切换相关的更多相关文章

  1. iOS 实现快速切换主题详细教程(附上源码)

    前言 iOS 实现主题切换,相信在未来的app里也是会频繁出现的,尽管现在只是出现在主流的APP,如(QQ.新浪微博.酷狗音乐.网易云音乐等),但是现在是看颜值.追求个性的年代,所以根据用户喜好自定义 ...

  2. 解决iOS设备屏幕切换时页面造成的问题

    环境:IOS6~7 Safari 问题:iOS设备屏幕切换时可能会造成屏幕变大,出现左右间距等问题 解决方法: 头部加入<meta name = "viewport" con ...

  3. 转:基于IOS上MDM技术相关资料整理及汇总

    一.MDM相关知识: MDM (Mobile Device Management ),即移动设备管理.在21世纪的今天,数据是企业宝贵的资产,安全问题更是重中之重,在移动互联网时代,员工个人的设备接入 ...

  4. 基于IOS上MDM技术相关资料整理及汇总

    (转自:http://www.mbaike.net/special/1542.html) 一.MDM相关知识:MDM (Mobile Device Management ),即移动设备管理.在21世纪 ...

  5. iOS键盘中英文切换键盘高度获取通知方法

    iOS键盘中英文切换键盘高度获取通知方法, 有需要的朋友可以参考下. 注册通知 - (void)viewWillAppear:(BOOL)animated { [super viewWillAppea ...

  6. 在uwp仿IOS的页面切换效果

    有时候我们需要编写一些迎合IOS用户使用习惯的uwp应用,我在这里整理一下仿IOS页面切换效果的代码. 先分析IOS的页面切换.用户使用左右滑动方式进行前进和后退,播放类似于FlipView的切换动画 ...

  7. iOS音乐播放器相关

    iOS音乐播放器框架主要有两大类:AvPlayer.AvaudioPlayer AvPlayer 能播放本地及网络歌曲 AvaudioPlayer 能播放本地歌曲.有相关代理方法(其实也可以播放网络歌 ...

  8. iOS 10 开发 相机相关的适配

    升级 iOS 10 之后目测坑还是挺多的,记录一下吧,看看到时候会不会成为一个系列. 直入正题吧 今天在写 Swift 3 相关的一个项目小小练下手,发现调用相机,崩了.试试看调用相册,又特么崩了.然 ...

  9. Xamarin开发IOS笔记:切换输入法时输入框被遮住

    在进行IOS开发的过程中,出现类似微信朋友圈的交互界面,当用户遇到感兴趣的内容可以进行评论.为了方便评论输入,当出现评论输入框的时候自动将评论输入框移动至键盘的上方,这样方便边输入边查看. 当用户隐藏 ...

随机推荐

  1. jQuery 回调函数

    jQuery(回调函数) 此函数的作用将callback参数以函数的定义形式,在页面onload的时候进行调用.相当于$(document).ready(callback). <script t ...

  2. OpenCV亚像素级的角点检测

    亚像素级的角点检测 目标 在本教程中我们将涉及以下内容: 使用OpenCV函数 cornerSubPix 寻找更精确的角点位置 (不是整数类型的位置,而是更精确的浮点类型位置). 理论 代码 这个教程 ...

  3. OpenGL入门学习(转载)

    说起编程作图,大概还有很多人想起TC的#include <graphics.h>吧? 但是各位是否想过,那些画面绚丽的PC游戏是如何编写出来的?就靠TC那可怜的640*480分辨率.16色 ...

  4. 杭电 HDU 1031 Design T-Shirt

    Design T-Shirt Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) T ...

  5. [AngularJS] Angular 1.3 Anuglar hint

    Read More: http://www.linkplugapp.com/a/953215 https://docs.google.com/document/d/1XXMvReO8-Awi1EZXA ...

  6. APNS 生成证书 p12 或者 PEM

    .net环境下须要p12文件,下面是生成p12过程 1.$ openssl x509 -in aps_development.cer -inform der -out PushChatCert.pem ...

  7. android源代码在线阅读

    http://grepcode.com/project/repository.grepcode.com/java/ext/com.google.android/android/

  8. UNIX网络编程读书笔记:TCP输出、UDP输出和SCTP输出

    TCP输出 下图展示了应用进程写数据到TCP套接口的过程. 每一个TCP套接口有一个发送缓冲区,我们可以用SO_SNDBUF套接口选项来改变这个缓冲区的大小. 当应用进程调用write时,内核从应用进 ...

  9. <续>调度算法补充

    cpmpute->executors: 1.从storm配置获取<compoent-id,parallelism>集合 2.storm-task-info 获得<task-id ...

  10. python selenium 自动化测试环境安装

    注意:2.7和3.0版本的语法有些不一样 安装自动化测试软件 selenium(地址http://www.seleniumhq.org/download/) 安装步骤: 1.安装pythone运行环境 ...