保存和恢复过程
状态保存和恢复是可选的功能,并需要在应用的帮助下工作。应用辨认出那些应该被存储的对象,UIKit会在恰当的时候执行保存和恢复这些对象的工作。因为UIKit处理如此多的过程,所以理解它在幕后的工作对于你知道自定义代码是如何适应整体方案有帮助。
当思考状态保存和恢复的时候,分成两部分进行是有帮助的。UIKit在恰当的时间保存应用状态,例如当应用从前台进入后台。当UIKit确定新状态信息是必须的时候,它查看应用的视图和视图控制器,以便知道哪些应该被保存。对于每个这样的对象,UIKit把存储相关的数据写入加密的磁盘文件中。下一次,当应用重新启动的时候,UIKit寻找这些文件,如果存在,使用它们来恢复应用的状态。因为这些文件是加密的,所以状态的保存和恢复必须是在设备解锁的状态下进行。
在恢复状态期间,UIKit使用保存的数据来重新构建界面,但实际对象的创建由你的代码完成。因为应用或许从storyboard文件自动加载对象,只有你的代码知道哪个对象需要被创建,而哪个对象已经存在可以简单的返回。在创建了每个对象之后,UIKit使用存储的状态信息初始化它们。
在存储和恢复状态期间,应用有一些责任。
- 在存储期间,应用的责任有:
- 包塑UIKit它支持状态存储。
- 告诉UIKit哪个视图控制器和视图应该被保存。
- 为任何被保存的对象编码相关数据。
- 在恢复期间,应用的责任:
- 告诉UIKit它支持状态恢复。
- 提供(或创建)那些被UIKit要求的对象。
- 返回对象到它之前的状态。
在应用的责任方面,最重要的就是告诉UIKit哪个对象需要保存,并在以后应用启动期间提供者这些对象。这两个行为放置在何处,是你在设计应用保存和恢复代码的时候需要花大量时间思考的。它们也是你对实际过程最有力的控制。为了理解为什么是这样,看一个例子会有所帮助。
图5-1 展示了有多个选项卡的选项卡栏界面视图控制器的层级结构。正如你看到的,一些视图控制器作为应用主storyboard文件被自动加载,但是另一些视图控制器却没有。在没有状态恢复功能的情况下,只有从主storyboard文件加载的视图控制器会在重新启动时恢复状态。但通过添加支持状态恢复功能到你的应用,你可以保留所有视图控制器
图5-1 视图控制器层级结构样本
UIKit只保存那些分配有恢复标识符的对象。恢复标识符是一个字符串,它用于标识UIKit和应用的视图和视图控制器。这个字符串的值仅对你的代码有意义,但是此字符串的存在告诉UIKit它需要保存标记的对象。在保存过程期间,UIKit会巡视应用的控制器层级并保存所有具有恢复标识符的对象。如果视图控制器没有恢复标识符,这些视图控制器以及它们的自视图和子视图控制器不会被保存。图5-2展示了之前视图层级结构的更新版本,现在大多(不是全部)数视图控制器都有了恢复标识符。
图5-2 给视图控制器添加恢复标识符
根据应用的需要,保存每个视图控制器有可能没有意义。如果视图控制器出现一个临时的信息,你或许不想在返回的时候还看到它,而是希望给用户一个更稳定的界面。
如果选择保存所有视图控制器的状态,你也需要决定你如何在之后恢复它们。UIKit提供两种创建对象的方式。你可以让应用的委托创建它们,或者可以为视图控制器分配一个恢复类并让这个类来创建它们。一个恢复类要实现UIViewControllerRestoration协议,并且在恢复时负责查找和创建指定的对象。下面有这对每种情况的一些提示:
- 如果视图控制器在启动时始终从主storyboard文件加载,不要分配恢复类。取而代之的是,让应用的委托来查找这个对象,或者充分利用UIKit的支持来隐式查找恢复的对象。
- 对于启动时不从主storyboard文件加载的视图控制器,分配一个恢复类。最简单的方式是让每个视图控制器成为自身的恢复类。
在存储过程期间,UIKit识别要保存的对象,并把每个受影响对象的状态写入磁盘。每个视图控制器对象都有一个机会来写入它们想保存的任何数据。例如,一个选项卡视图控制器保存被选中的选项卡的标识符。UIKit也保存像视图控制器的恢复类这样的信息到磁盘。如果每个视图控制器的视图都有恢复标识符,UIKit也会要求它们保存状态信息。
在下次应用启动的时候,UIKit如常的加载应用的主storyboard或nib文件,调用应用委托的application:willFinishLaunchingWithOptions:方法,然后尝试恢复应用之前的状态。首先,它会要求应用程序提供与存储的匹配的一系列视图控制器对象。如果给丁的视图控制器有分配恢复类,这个恢复类会被要求提供对象;否则,应用委托将被要求提供对象。
保存过程的流程
图5-3 展示了在状态保存期间发生的高级事件,还显示了应用的对象如何被影响。在保存事件发生之前,UIKit会要求应用委托调用application:shouldSaveApplicationState:方法。如果这个方法返回YES,UIKit开始收集并编码应用的视图和视图控制器。当此过程完成时,它把编码的数据写入磁盘。
图5-3 界面保存高层级工作流
在下一次应用启动的时候,系统自动查找存储状态的文件,如果存在,使用它们来恢复界面。因为此状态信息只和应用的上次及本次启动周期有关,所以该文件通常在应用启动完成后被丢弃。这个文件也会因恢复错误而丢弃。例如,如果应用在恢复的过程中崩溃,系统就会在下次启动周期期间自动的丢弃状态信息来防止再一次崩溃。
恢复过程的流程
图5-4展示了在状态恢复期间发生的高级事件,以及展示了应用的对象如何被影响。在标准的初始化和UI加载完成后,UIKit会通过调用application:shouldRestoreApplicationState:方法来询问应用程序委托,状态恢复是否可以进行。这是应用的委托检查保存的数据以及决定是否有可能进行状态恢复的机会。如果是,UIKit使用应用委托和状态恢复类来获取应用视图控制器的引用。然后每个对象就使用它需要的数据恢复到之前的状态。
图5-4 恢复应用用户界面的高级流程
虽然UIKit帮助恢复各个视图控制器,但它不能自动恢复这些视图控制器之间的关系。每个视图控制器要负责编码足够的状态信息以便能使自己恢复到之前的状态。例如,一个导航控制器编码在导航栈中的视图控制器的顺序。然后在以后使用这些信息来返回到之前的状态。其他嵌套子视图控制器的视图控制器也有类似的编码责任。
注意:不是所有的视图控制器都需要编码它们的子视图控制器。例如,选项栏视图控制器。它假设应用遵循优先创建合适的自视图控制器,然后再创建标签栏控制器的常规模式。
因为你负责重新创建应用的视图控制器,所以你再恢复过程期间有一些灵活的方式来改变应用的界面。例如,你可以重新对选项栏控制器的选项卡进行重排序,而且仍然可以使用存储的数据来恢复到之前的状态。当然,如果你对视图控制器层级进行了显著的更改,例如在应用更新期间,你可能无法使用存储的数据。
当你排除视图控制器群组时会发生什么?
当视图控制器的恢复标识符为nil时,视图控制器和它的自视图控制器不会自动保存。例如,在图5-5中,因为导航控制器没有恢复标识符,它和它的所有子视图控制器及视图会从保存的数据中被删除。
图5-5 从自动存储过程中排除视图控制器
即使你决定不保存视图控制器,也不意味着这些视图控制器会完全从视图层级中消失。在启动的时候,应用仍然会以默认设置的方式创建这些视图控制器。例如,如果任何从应用的storyboard文件自动加载的视图控制器,它们让然会出现,尽管它们时默认的配置。如图5-6所示。
图5-6 加载默认的一组视图控制器
还有一点需要注意,即使视图控制器没有自动保存,你仍然可以对该视图控制器的引用进行编码并进行手动存储。在图5-5中,第一个导航控制器有三个子视图控制器有恢复标识符,即使父视图控制器没有。如果应用的委托(或者任何保存对象)编码这些视图控制器的引用,它们的状态也会被保存。即使它们在导航控制器的顺序没有被保存,你仍能够使用这些引用来创建这些视图控制器,并在后续的启动周期期间安装它们到导航控制器。
实现状态保存和恢复的检查清单
支持状态保存和恢复要求修改应用委托和视图控制器对象来编码和解码状态信息。如果应用有任何自定义的视图,就也需要可保存状态的信息。你需要也需要修改这些对象。
当在代码中添加状态保存和恢复内容时,使用以下列表来提醒你需要编写的代码。
- (要求)实现应用委托的application:shouldSaveApplicationState: 和 application:shouldRestoreApplicationState:方法。参见 Enabling State Preservation and Restoration in Your App。
- (要求)给每个你想保存的视图控制器分配恢复标识符,该标识符不能为空字符串,分配的位置是视图控制器的restorationIdentifier属性;参见Marking Your View Controllers for Preservation。如果你也想保存特定视图的状态,分配非空字符串给它们的restorationIdentifier属性;参见Preserving the State of Your Views。
- (要求)从应用委托的application:willFinishLaunchingWithOptions:方法展示应用的视窗。状态恢复机制需要视窗,以便应用能够恢复滚动位置以及其他相关的应用界面。
- 分配恢复类给合适的视图控制器。(如果你没有这样做,应用的委托会在恢复的时候被要起提供相关的视图控制器。)参见Restoring Your View Controllers at Launch Time。
- (推荐)使用视图和视图控制器的encodeRestorableStateWithCoder: 和 decodeRestorableStateWithCoder:来编解码它们的状态。参见Encoding and Decoding Your View Controller’s State。
- 使用应用委托的application:willEncodeRestorableStateWithCoder: 和 application:didDecodeRestorableStateWithCoder:方法来为应用编解码版本信息和额外的状态信息。参见 Preserving Your App’s High-Level State。
- 作为表视图以及集合视图的数据源的对象,应该实现UIDataSourceModelAssociation协议。虽然不是必须的,但是这个协议可以帮助保存和选择在这些类型的视图中的可见项目。参见Implementing Preservation-Friendly Data Sources。
在应用中启用状态保存和恢复
状态保存和恢复不是自动功能,应用必须选择使用它。应用通过在它们应用代理中实现下面的两个方法来支持这个功能:
application:shouldSaveApplicationState:
application:shouldRestoreApplicationState:
通常,你只要让这些方法返回YES就表明这个功能能够发生。但是,如果不想让此功能发生,可以让方法返回NO。例如,在释放和更新应用之后,如果应用不能有效的从之前的版本回复状态,你可以让application:shouldRestoreApplicationState:方法返回NO。
存储视图控制器的状态
存储应用的视图控制器的状态应该是你的主要目标。视图控制器定义了用户界面的结构。它们管理必要的视图来显示界面,并协调处理从这些视图返回的数据。为了存储单视图控制器的状态,你必须做到以下几点:
- (要求)分配恢复标识符给视图控制器,参见Marking Your View Controllers for Preservation。
- (要求)提供在应用启动的时候创建或定位新视图控制器的对象;参见Restoring Your View Controllers at Launch Time。
- (可选)实现encodeRestorableStateWithCoder: 和 decodeRestorableStateWithCoder:方法来编码和恢复任何状态信息,这些信息在下一次启动期间不会被重新创建;参见Encoding and Decoding Your View Controller’s State。
(未完待续......)