在状态恢复期间创建和恢复 UIViewControllers 的正确方法?

Posted

技术标签:

【中文标题】在状态恢复期间创建和恢复 UIViewControllers 的正确方法?【英文标题】:Correct way to create and restore UIViewControllers during state restoration? 【发布时间】:2013-07-18 22:30:02 【问题描述】:

我想在不使用故事板的应用中进行状态恢复。我看到我的主应用 ViewController 在状态恢复期间被实例化了两次 - 你如何确保它只创建一次?

按照我理解流程的方式,application:willFinishLaunchingWithOptionspplication:didFinishLaunchingWithOptions 将使用commonInit 方法来设置应用程序 UIWindow 及其 rootViewController。在我的例子中,rootViewController 是一个 UINavigationController,其中一个名为“MyMainViewController”的类用作 UINavigation 的 rootViewController。

除此之外,我还分别处理willEncodeRestorableStateWithCoderdidDecodeRestorableStateWithCoder。但似乎当我到达我的didDecodeRestorableStateWithCoder 时,我看到创建了两个独立的 MyMainViewController 实例。

如何保证恢复时只创建一个UIViewController?

恢复期间的调用顺序:

通过应用程序创建新实例 MyMainViewController (#1):willFinishLaunchingWithOptions: MyMainViewController 的 viewControllerWithRestorationIdentifierPath:coder 调用和 MainViewController 恢复(#2) application:didDecodeRestorableStateWithCoder: 被调用,UINavigationController被解码并分配给self.window

这是我在 AppDelegate 中所做的:

NSString * const kRootViewControllerKey = @"RootViewControllerKey";

- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
    [self commonInitWithOptions:launchOptions];
    return YES;


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
    [self commonInitWithOptions:launchOptions];
    return YES;


- (void)commonInitWithOptions:(NSDictionary *)launchOptions 

    static dispatch_once_t predicate;
    dispatch_once(&predicate, ^ 

        // While this will be called only once during the lifetype of the app, when the process is killed 
        // and restarted, I wind up with an instance of MyMainViewController created first from here
        // and then once again, during MyMainViewController's viewControllerWithRestorationIdentifierPath:coder 
        // that's invoked later on. 

        UIViewController *rootViewController = [MyMainViewController alloc] init];
        UINavigationController *aNavController = [[UINavigationController alloc] initWithRootViewController:rootViewController];

        aNavController.navigationBarHidden = YES;
        aNavController.restorationIdentifier = NSStringFromClass([aNavController class]);

        UIWindow *aWindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
        aWindow.rootViewController = aNavController;
        aWindow.restorationIdentifier = NSStringFromClass([window class]);

        self.window = aWindow;
    );


// Encode app delegate level state restoration data
- (void)application:(UIApplication *)application willEncodeRestorableStateWithCoder:(NSCoder *)coder 
    [coder encodeObject:self.window.rootViewController forKey:kRootViewControllerKey];


// Decode app delegate level state restoration data
- (void)application:(UIApplication *)application didDecodeRestorableStateWithCoder:(NSCoder *)coder 

    // Find the preserved root view controller and restore with it
    UINavigationController *navControlller = [coder decodeObjectForKey:kRootViewControllerKey];

    if (navControlller) 
        self.window.rootViewController = navControlller;
    


【问题讨论】:

你找到解决方法了吗?我遇到了完全相同的问题,看到我的视图控制器被初始化了两次。 不——从来没有。我不知道如何解决它,因为我无法使用情节提要。 【参考方案1】:

我的根视图类只应该有一个实例,所以我通过添加一个类方法来解决它,只分配一次并初始化该类,否则返回值:

+ (id) initOnce 
    static id view_ref;

    if(!view_ref)
        view_ref = [[UIViewController alloc] init];

    return view_ref;

现在,当通过 [UIViewController initOnce] 初始化类时,始终返回相同的视图引用,无论是在 willFinishLaunchingWithOptions 还是 viewControllerWithRestorationIdentifierPath 期间。

【讨论】:

另外,你不能在根视图上设置 .restorationClass 并且 UIKit 状态恢复似乎是做“正确的事情”。

以上是关于在状态恢复期间创建和恢复 UIViewControllers 的正确方法?的主要内容,如果未能解决你的问题,请参考以下文章

授权失败,Pod 在从 Velero Backup 恢复期间停滞在“Init:0/1”状态

状态恢复的状态栏样式

Swift 中的 iOS 8 和 9:首次启动期间 [警告:无法创建正在进行的恢复标记文件]

FLINK重点原理与机制:状态从一致检查点中恢复状态

从核心文件恢复程序状态

UIViewControllers 正在创建但未在状态恢复中显示