处理应用程序状态转换的策略
对于应用程序的每个可能的运行时状态,系统在您的应用程序处于该状态时具有不同的期望值。当状态转换发生时,系统会通知应用对象,该对象又通知其应用代理。您可以使用UIApplicationDelegate协议的状态转换方法来检测这些状态变化并进行适当的响应。例如,当从前台转移到后台时,您可能会写出任何未保存的数据,并停止任何正在进行的任务。以下部分提供了有关如何实现状态转换代码的提示和指导。
在启动时怎么办
当您的应用程序启动(前进或后台)时,请使用应用程序委托应用程序:willFinishLaunchingWithOptions:和application:didFinishLaunchingWithOptions:执行以下操作的方法:
检查启动选项字典的内容,了解应用程序启动的原因,并进行适当的响应。
初始化您的应用程序的关键数据结构。
准备您的应用程序的窗口和视图进行显示:
使用OpenGL ES进行绘图的应用程序不得使用这些方法来准备绘图环境。相反,将任何OpenGLES绘图调用延迟到applicationDidBecomeActive:方法。
从您的应用程序显示您的应用程序窗口:willFinishLaunchingWithOptions:method。UIKit延迟使窗口可见,直到应用程序之后:didFinishLaunchingWithOptions:method返回。
在启动时,系统自动加载应用程序的主要故事板文件,并加载初始视图控制器。对于支持状态恢复的应用程序,状态恢复机制将您的界面恢复到对应用程序的调用之前的状态:willFinishLaunchingWithOptions:andapplication:didFinishLaunchingWithOptions:methods。使用应用程序:willFinishLaunchingWithOptions:方法显示您的应用程序窗口,并确定状态恢复是否应该发生。使用应用程序:didFinishLaunchingWithOptions:方法对应用程序的用户界面进行任何最终调整。
您的应用程序:willFinishLaunchingWithOptions:和应用程序:didFinishLaunchingWithOptions:方法应始终尽可能轻量级,以减少您的应用程序的启动时间。应用程序预计将启动,初始化自己,并在不到5秒钟内开始处理事件。如果应用程序没有及时完成其启动周期,系统将使其无法响应。因此,任何可能减慢启动速度(例如访问网络)的任务都应该在辅助线程上执行。
启动周期
当您的应用程序启动时,它将从非运行状态移动到活动状态或背景状态,暂时转换到非活动状态。作为启动周期的一部分,系统为您的应用程序创建一个进程和主线程,并在该主线程上调用您的应用程序的主要功能。您的Xcode项目随附的默认主要功能可以手动控制到UIKit框架,这在大多数工作中初始化您的应用程序并准备运行。
图4-1显示应用程序启动到前台时发生的事件序列,包括调用的应用程序代理方法。
图4-1将应用程序启动到前台
当您的应用程序启动到后台时 - 通常来处理某些类型的背景事件 - 启动周期会略有变化,如图4-2所示。主要区别在于,您的应用程式不是活跃的,而是进入后台状态来处理该事件,之后可能会被暂停。 当启动到后台时,系统仍然会加载您的应用程序的用户界面文件,但不会显示应用程序的窗口。
图4-2将应用程序启动到后台
要确定您的应用程序是启动到前台还是后台,请检查应用程序中共享的UIApplication对象的applicationState属性:willFinishLaunchingWithOptions:或application:didFinishLaunchingWithOptions:delegate方法。当应用程序启动到前台时,此属性包含值UIApplicationStateInactive。当应用程序启动到后台时,该属性包含值UIApplicationStateBackground。您可以使用此差异来相应地调整委托方法的启动时间行为。
注意:启动应用程序以便可以打开URL时,启动事件的顺序与图4-1和图4-2所示的顺序不同。有关打开URL时启动序列的信息,请参阅处理URL请求。Handling URL Requests
以横向模式启动
仅使用其界面的横向应用程序的应用程序必须明确要求系统以该方向启动应用程序。通常,应用程序以纵向模式启动,并根据需要旋转其界面以匹配设备方向。对于支持纵向和横向方向的应用程序,请始终为纵向模式配置视图,然后让视图控制器处理任何旋转。但是,如果您的应用程序支持横向,但不支持纵向方向,请执行以下任务,以使其以横向模式启动:
将UIInterfaceOrientation键添加到应用程序的Info.plist文件中,并将该键的值设置为UIInterfaceOrientationLandscapeLeft或UIInterfaceOrientationLandscapeRight。
在横向模式下放置视图,并确保其布局或自动调整选项设置正确。
覆盖您的视图控制器的shouldAotorotateToInterfaceOrientation:方法,并为左侧或右侧横向方向返回YES,然后为纵向方向返回NO。
重要提示:应用程序应始终使用视图控制器来管理其基于窗口的内容。
Info.plist文件中的UIInterfaceOrientation键告诉iOS应该配置应用状态栏的方向(如果显示)以及任何视图控制器在启动时管理的视图的方向。视图控制器尊重此键并设置其视图的初始方向进行匹配。使用此键相当于在执行您的applicationDidFinishLaunching:方法时调用setStatusBarOrientation:animated:UIApplication方法。
首次启动时安装应用专用数据文件
您可以使用应用程序的第一个启动周期来设置运行所需的任何数据或配置文件。应用程序特定的数据文件应在应用程序沙箱的库/应用程序支持/ /目录中创建,其中是应用程序的捆绑标识符。您可以根据需要进一步细分此目录来组织数据文件。您还可以根据需要在其他目录中创建文件,如应用程序的iCloud容器目录或本地Documents目录。
如果您的应用程序包包含您打算修改的数据文件,请将这些文件从应用程序包中复制出来并修改副本。您不得修改应用程序包内的任何文件。由于iOS应用程式是代码签名的,因此修改应用程式包中的档案会使应用程式的签名无效,并将阻止您的应用程式在将来启动。将这些文件复制到应用程序支持目录(或沙箱中的另一个可写目录),并将其修改为安全使用这些文件的唯一方法。
有关放置应用程序相关数据文件的位置的更多信息,请参阅“文件系统编程指南”。File System Programming Guide.
当你的应用程序暂时中断时该怎么办
基于警报的中断导致您的应用程序暂时失去控制权。您的应用程序继续在前台运行,但不会从系统接收到触摸事件。(它确实继续接收通知和其他类型的事件,例如加速度计事件。)为了响应此更改,您的应用程序应在其ApplicationWillResignActive:方法中执行以下操作:
保存数据和任何相关状态信息。
停止计时器和其他定期任务。
停止任何运行的元数据查询
不要启动任何新任务。
暂停播放电影(在AirPlay上播放时除外)。
如果您的应用是游戏,请进入暂停状态。
打开OpenGL ES帧速率。
暂停执行非关键代码的任何调度队列或运行队列。 (您可以在不活动的情况下继续处理网络请求和其他时间敏感的后台任务。)
当您的应用程序移回到活动状态时,其applicationDidBecomeActive:方法应该可以扭转applicationWillResignActive:方法中执行的任何步骤。因此,重新启动后,您的应用程序应重新启动计时器,恢复调度队列,并再次调低OpenGLES帧速率。但是,游戏不应该自动恢复;它们应该保持暂停,直到用户选择恢复它们为止。
当用户按下睡眠/唤醒按钮时,具有受NSFileProtectionComplete保护选项保护的文件的应用程序必须关闭对这些文件的任何引用。对于配置了相应密码的设备,按睡眠/唤醒按钮会锁定屏幕,并强制系统丢弃具有完全保护功能的文件的解密密钥。当屏幕被锁定时,任何访问相应文件的尝试将失败。所以如果你有这样的文件,你应该在你的applicationWillResignActive:方法中关闭对它们的任何引用,并在你的applicationDidBecomeActive:方法中打开新的引用。
重要提示:始终将应用程序中的用户数据保存在适当的检查点。虽然您可以使用应用程序状态转换来强制对象将未保存的更改写入磁盘,但不要等待应用程序状态转换来保存数据。例如,管理用户数据的视图控制器应在其被忽略时保存其数据。
响应临时中断
当出现基于警报的中断(例如来电)时,应用程序暂时移动到非活动状态,以便系统可以提示用户如何继续。该应用保持在此状态,直到用户关闭警报。此时,应用程序返回到活动状态或移动到背景状态。图4-3显示了当发生基于警报的中断时,通过应用程序的事件流程。
图4-3处理基于警报的中断
显示横幅的通知不会以基于警报的通知的方式停用您的应用程序。相反,横幅放置在应用程序窗口的顶部边缘,您的应用程序将继续像以前一样接收触摸事件。但是,如果用户拉下横幅以显示通知中心,则您的应用程序将移动到非活动状态,就像发生基于警报的中断一样。您的应用程式仍处于停用状态,直到使用者解除通知中心或启动其他应用程式。此时,您的应用程序将移动到相应的活动或背景状态。用户可以使用“设置”应用来配置哪些通知显示横幅并显示警报。
按睡眠/唤醒按钮是另一种类型的中断,导致临时停用应用程序。当用户按此按钮时,系统会禁用触摸事件,将应用程序移动到后台,将应用程序的applicationState属性的值设置为UIApplicationStateBackground,并锁定屏幕。锁定屏幕会对使用数据保护加密文件的应用程序带来额外的后果。这些后果在您的应用程序暂时中断时应该怎么做。
当您的应用程序进入前台时该怎么办
回到前台是您的应用程序重新启动它移动到后台停止的任务的机会。移动到前景时出现的步骤如图4-4所示。applicationWillEnterForeground:方法应该撤消在applicationDidEnterBackground:method中完成的任何内容,而applicationDidBecomeActive:方法应该继续执行与启动时相同的激活任务。
图4-4从背景转移到前景
注意:当您的应用程序重新进入前景时,UIApplicationWillEnterForegroundNotification通知也可用于跟踪。您的应用程序中的对象可以使用默认通知中心注册此通知。
准备处理排队通知
处于挂起状态的应用程序必须准备好处理任何排队的通知,当它返回到前台或后台执行状态。暂停的应用程序不执行任何代码,因此无法处理与方向更改,时间更改,偏好更改以及影响应用的外观或状态的其他许多相关的通知。为了确保这些更改不会丢失,系统会排队许多相关的通知,并在应用程序再次开始执行代码(前台或后台)时将其发送到应用程序。为了防止您的应用程序在恢复通知时变得超负荷,系统会将事件合并,并提供一个单一的相关类型的通知(反映自应用程序被暂停后的净更改)。
表4-1列出了可以合并并交付给您的应用程序的通知。这些通知中的大多数都直接发送给已注册的观察员。一些与设备方向更改相关的内容通常被系统框架截取,并以另一种方式传送到您的应用程序。
表4-1提供给醒来的应用程序的通知
事件
通知
连接或断开附件。
EAAccessoryDidConnectNotification
EAAccessoryDidDisconnectNotification
设备方向改变。
UIDeviceOrientationDidChangeNotification
除了此通知之外,视图控制器会自动更新其界面方向。
有一个显着的时间变化。
UIApplicationSignificantTimeChangeNotification
电池电量或电池状态发生变化。
UIDeviceBatteryLevelDidChangeNotification
UIDeviceBatteryStateDidChangeNotification
接近状态发生变化。
UIDeviceProximityStateDidChangeNotification
受保护文件的状态发生变化。
UIApplicationProtectedDataWillBecomeUnavailable
UIApplicationProtectedDataDidBecomeAvailable
外部显示器被连接或断开。
UIScreenDidConnectNotification
UIScreenDidDisconnectNotification
显示屏的画面模式发生变化。
UIScreenModeDidChangeNotification
您的应用程序通过“设置”应用程序暴露的首选项已更改。
NSUserDefaultsDidChangeNotification
当前语言或区域设置已更改。
NSCurrentLocaleDidChangeNotification
用户的iCloud帐户的状态已更改。
NSUbiquityIdentityDidChangeNotification
排队的通知在应用程序的主运行循环中传递,通常在任何触摸事件或其他用户输入之前提供。大多数应用程序应该能够快速处理这些事件,以至于在恢复时不会造成任何明显的滞后。但是,如果您的应用程序从后台状态返回时显示为缓慢,请使用“仪器”来确定您的通知处理程序代码是否导致延迟。
返回前台的应用程序也会收到自上次更新以来被标记为脏的任何视图的视图更新通知。在后台运行的应用程序仍然可以调用setNeedsDisplay或setNeedsDisplayInRect:方法来请求更新其视图。但是,由于视图不可见,系统会将请求合并,并在应用程序返回前台后更新视图。
处理iCloud更改
如果iCloud的状态出于任何原因而更改,系统会向您的应用提供NSUbiquityIdentityDidChangeNotification通知。用户登录或退出iCloud帐户时,iCloud的状态会发生变化,或启用或禁用文档和数据的同步。此通知是您的应用程序提示更新缓存和任何与iCloud相关的用户界面元素以适应更改。例如,当用户注销iCloud时,应删除对所有基于iCloud的文件或数据的引用。
如果您的应用程序已经提示用户是否在iCloud中存储文件,请在iCloud状态更改时再次提示。在第一次提示用户后,将用户的选择存储在应用程序的本地首选项中。然后,您可能需要使用设置包或应用程序中的选项来显示该偏好。但不要再次重复提示,除非该偏好目前不在用户默认数据库中。
处理区域设置更改
如果用户在应用程序暂停时更改当前区域设置,则可以使用NSCurrentLocaleDidChangeNotification通知来强制更新任何包含区域设置敏感信息的视图,例如,当应用程序返回到前台时,日期,时间和数字。当然,避免与语言环境有关的问题的最好办法就是编写代码,方便您更新视图。例如:
检索NSLocale对象时,请使用autoupdatingCurrentLocale类方法。此方法返回一个自动更新自身以响应更改的区域设置对象,因此您不需要重新创建它。但是,当区域设置更改时,您仍然需要刷新包含从当前区域设置派生的内容的视图。
每当当前的区域设置信息发生变化时,重新创建任何缓存的日期和数字格式化对象。
有关将代码国际化以处理区域设置更改的更多信息,请参阅国际化和本地化指南。Internationalization and Localization Guide.
处理您应用程序设置的更改
如果您的应用具有由设置应用程序管理的设置,则应遵循NSUserDefaultsDidChangeNotification通知。由于用户可以在您的应用程序被暂停或在后台修改设置,因此您可以使用此通知来响应这些设置中的任何重要更改。在某些情况下,响应此通知可以帮助关闭潜在的安全漏洞。例如,电子邮件程序应该响应用户帐户信息的更改。不监视这些更改可能会导致隐私或安全问题。具体来说,即使帐户不再属于该用户,当前用户也可以使用旧的帐户信息发送电子邮件。
在收到NSUserDefaultsDidChangeNotification通知后,您的应用程序应重新加载任何相关设置,如有必要,请重新设置用户界面。如果密码或其他安全相关信息发生变化,您还应隐藏任何先前显示的信息,并强制用户输入新密码。
当您的应用程序进入后台时该怎么办
从前台执行到后台执行时,请使用应用程序委托的applicationDidEnterBackground:方法执行以下操作:
准备好应用程序的图片。当您的applicationDidEnterBackground:方法返回时,系统会拍摄您应用的用户界面,并使用生成的图像进行转换动画。如果界面中的任何视图包含敏感信息,则应在applicationDidEnterBackground:method返回之前隐藏或修改这些视图。如果您在视图层次结构中添加新视图作为此过程的一部分,则必须强制这些视图绘制自己,如“准备应用程序快照”中所述。Prepare for the App Snapshot.
保存任何相关的应用状态信息。在进入后台之前,您的应用程序应该已经保存了所有关键的用户数据。使用转换到背景来保存应用程序状态的最后一分钟更改。
根据需要释放内存释放您不需要的任何缓存数据,并执行可能会减少应用程序内存占用的简单清理。具有大内存占用的应用程序是系统首先要终止的,因此可以释放图像资源,数据高速缓存以及您不再需要的任何其他对象。有关更多信息,请参阅减少内存占用。Reduce Your Memory Footprint.
您的应用程序委托的applicationDidEnterBackground:方法大约需要5秒钟完成任务并返回。实际上,这种方法应尽可能快地返回。如果方法在时间用完之前没有返回,您的应用程序将被从内存中删除。如果还需要更多时间来执行任务,请调用beginBackgroundTaskWithExpirationHandler:方法来请求后台执行时间,然后在辅助线程中启动任何长时间运行的任务。无论是否启动任何后台任务,applicationDidEnterBackground:方法必须在5秒内退出。
注意:除了调用applicationDidEnterBackground:方法外,系统还会发送UIApplicationDidEnterBackgroundNotification通知。您可以使用该通知将清理任务分发到应用程序的其他对象。
根据应用程序的功能,应用程序在移动到背景时应该执行其他操作。例如,任何活动的Bonjour服务应该被暂停,应用程序应该停止调用OpenGLES函数。有关您的应用程序在移动到背景时应该执行的操作的列表,请参阅作为负责任的后台应用程序。Being a Responsible Background App
后台过渡周期
当用户按下“主页”按钮时,按睡眠/唤醒按钮,或者系统启动另一个应用程序,前台应用程序转换到非活动状态,然后转换到背景状态。这些转换导致调用app代理的applicationWillResignActive:和applicationDidEnterBackground:方法,如图4-5所示。从applicationDidEnterBackground:方法返回后,大多数应用程序不久之后就会移动到暂停状态。请求特定后台任务(如播放音乐)或从系统请求一点额外执行时间的应用程序可能会持续运行一段时间。
图4-5从前台移动到背景
准备应用程序快照
应用程序委托的applicationDidEnterBackground:方法返回后不久,系统会收到应用程序的Windows快照。同样,当应用程序被唤醒以执行后台任务时,系统可以采取新的快照来反映任何相关的更改。例如,当应用程序被唤醒以处理下载的项目时,系统将采取新的快照,以便可以反映由项目合并引起的任何更改。系统在多任务UI中使用这些快照图像来显示应用程序的状态。
如果在进入后台更改视图时,可以调用主视图的snapshotViewAfterScreenUpdates:方法来强制执行这些更改。在视图上调用setNeedsDisplay方法对快照无效,因为在下一个绘图周期之前拍摄快照,因此防止任何更改被渲染。调用值为“YES”的snapshotViewAfterScreenUpdates:method将立即更新快照机器使用的底层缓冲区。
减少你的记忆足迹
每个应用程序应该在进入后台释放尽可能多的内存。系统尝试尽可能多地保留内存中的应用程序,但是当内存不足时,它会终止挂起的应用程序以回收该内存。在后台消耗大量内存的应用程序是第一个要终止的应用程序。
实际上,您的应用程序应该在不再需要的时候删除对对象的强引用。删除强引用使编译器能够立即释放对象,以便可以回收对应的内存。但是,如果要缓存某些对象以提高性能,则可以等到应用程序转换到后台才能删除对它们的引用。
您应该尽快删除强引用的对象的一些示例包括:
您创建的图像对象。 (有些UIImage的方法可以返回由系统自动清除底层图像数据的图像。有关更多信息,请参阅UIImage类参考概述中的讨论。)
可以从磁盘重新加载的大型媒体或数据文件
您的应用程序不需要的任何其他对象,稍后可以重新创建
为了帮助您减少应用程序的内存占用空间,当应用程序移动到后台时,系统会自动清除代表应用程序分配的一些数据。
系统清除所有Core Animation层的后备存储。此功能不会从应用程序的图层对象中删除内存,也不会更改当前图层属性。它只是防止这些图层的内容出现在屏幕上,这表明该应用程序在后台应该不会发生。
它会删除对缓存图像的任何系统引用。
它删除了对其他系统管理的数据高速缓存的强引用。