本文介绍了,iOS的状态保存和复原流程。
UIKit控制整个状态保存和复原流程,UIKit会在需要的时刻执行保存操作,例如当App切到后台时,此时,UIKit判断App的Views和VCs哪些需要保存,然后将其数据写入加密的硬盘文件中,在App下次运行时,UIKit会检索该文件是否存在,存在则尝试复原App的状态。由于该文件是加密的,所以这一过程只会发生在设备已经解锁以后。
保存过程
在保存过程中,App需要:
- 告诉UIKit需要状态保存;
- 告诉UIKit那些VCs和Views需要保存;
- 将一些相关的对象的数据编码。
UIKit是根据Restoration Identifier是否设置来判断一个ViewController或者View是否要保存的。
下面是例子:
注意:如果一个VC没有设置Restoration Identifier,则其所有Views也不会被保存。这里,特别注意,如果一个NavigationController没有设置Restoration Identifier,则其所有的VC都不会被保存。
在保存过程中,UIKit识别哪些对象需要保存,并与影响该对象的状态一起写入硬盘,例如VC的Restoration Class。对于每个有Restoration ID的VCs和Views对象,将有一个机会保存一些自身的数据,UIKit会调用VC和View的以下方法:
1 | - (void)encodeRestorableStateWithCoder:(NSCoder *)coder |
对于特殊的TableView和CollectionView,其DataSource必须适配协议:UIDataSourceModelAssociation。
对于一些不在VC中,但是又需要保存的状态信息,可以在回调中保存:
1 | - (void)application:(UIApplication *)application willEncodeRestorableStateWithCoder:(NSCoder *)coder |
流程图:
复原过程
在复原过程中,App需要:
- 告诉UIKit需要状态复原;
- 提供或者创建UIKit要求的对象;
- 解码保存过程中编码的对象,用于状态复原。
在App重新启动时,UIKit加载App的Storyboard或Nib文件,调用application:willFinishLaunchingWithOptions:回调,然后尝试复原状态。如果不是从Storyboard或Nib文件,则UIKit会让App提供与之前保存的VCs对应的对象组合,如果一个VC有Restoration class,则直接让其提供VC对象,否则,让AppDelegate提供:
1 | - (UIViewController *)application:(UIApplication *)application viewControllerWithRestorationIdentifierPath:(NSArray *)identifierComponents coder:(NSCoder *)coder |
Restoration Class定义了UIViewControllerRestoration协议:
1 | @property (nullable, nonatomic, readwrite, assign) Class<UIViewControllerRestoration> restorationClass NS_AVAILABLE_IOS(6_0); |
这两个回调中的viewControllerWithRestorationIdentifierPath的参数,是指VC保存的路径,例如一个TabVC为A,里面第一个Tab是一个NavigationVC为B,其RootVC是C,则路径为:
1 | A/B/C,对应上面的数组,注意路径必须是唯一的,且不会产生歧义的。 |
对于可以从Storyboard里面自动加载的VC,则无需设置Restoration class,否则,最好对其设置Restoration class。
VC加载完成后,将会调用方法:
1 | - (void)decodeRestorableStateWithCoder:(NSCoder *)coder |
对于一些不在VC中,但是又需要复原的状态信息,可以在回调中复原:
1 | - (void) application:(UIApplication *)application didDecodeRestorableStateWithCoder:(NSCoder *)coder |
注意:无论App成功复原或者复原失败(例如Crash),上述的保存文件都将被丢弃。且,UIKit不会帮助保存VC之间的关系,例如NavigationController中的多个VC。在复原时,最好尽量完整恢复原来的VC结构树。
流程图:
例子
AppDelegate设置支持保存和复原
1 | - (BOOL)application:(UIApplication *)application shouldSaveApplicationState:(NSCoder *)coder |
VC支持状态保存和复原
1 | - (void)encodeRestorableStateWithCoder:(NSCoder *)coder |
通过Storyboard
如果是在Storyboard中的VC,直接在面板设置Restoration ID即可。
通过RestorationClass
1 | @interface TestVC ()<UIViewControllerRestoration> |
通过AppDelegate
1 | - (UIViewController *)application:(UIApplication *)application viewControllerWithRestorationIdentifierPath:(NSArray *)identifierComponents coder:(NSCoder *)coder |
提示
- 保存版本信息:最好在状态保存和复原中,自己保存一个App版本信息,以便版本变更时,可以进行控制;
- 不要保存数据信息:数据信息不应该保存在状态信息中,因为状态信息的文件无论是否复原成功都会被删除,这可能会导致异常;
- 不是所有VC都需要保存:例如密码输入页面可能是不需要保存的,复原的时候,直接复原到登录页面即可;
- 不要改变VC的Class信息:UIKit是根据Class信息来复原VC的,所有不要去改变VC的Class;
- 用户强制关闭App时,系统会自动删除保存的状态文件:用户通过多任务管理,关闭App时,以及在启动时Crash两次时,状态文件会被删除,所以,不要通过多任务管理调试,而通过XCode直接Kill App。