运行 初始化第一个视图控制器之前的代码(基于故事板的应用程序)

Run Code Before First View Controller is Initialized (Storyboard-based App)

我的应用每次启动时都需要执行一些清理任务 - 删除本地存储的旧数据 - 在显示初始视图控制器之前。

这是因为初始视图控制器 在初始化时加载 现有数据,并将其显示在 table 视图中。

我设置了几个断点,发现初始视图控制器的初始化器(init?(coder aDecoder: NSCoder))是运行 before application(_:didFinishLaunchingWithOptions) - even在 application(_:**will**FinishLaunchingWithOptions) 之前,准确地说。

可以将清理代码放在视图控制器初始化程序的最顶部,但我想将清理逻辑与任何特定屏幕分离。 该视图控制器可能有一天最终不是初始视图控制器。

覆盖应用程序委托的 init() 方法并将我的清理代码放在那里 确实 完成了工作,但是...

问题:

有没有更好/更优雅的方式?


Note: For reference, the execution order of the relevant methods is as follows:

  1. AppDelegate.init()
  2. ViewController.init()
  3. AppDelegate.application(_:willFinishLaunchingWithOptions:)
  4. AppDelegate.application(_:didFinishLaunchingWithOptions:)
  5. ViewController.viewDidLoad()

澄清:

清理任务并不冗长,不需要异步 运行:相反,我更愿意在完成之前甚至不实例化我的初始视图控制器(我 am 知道如果启动时间过长,系统会终止我的应用程序。但是,它只是删除一些本地文件,没什么大不了的。)。

我问这个问题主要是因为,在情节提要出现之前,应用程序启动代码如下所示:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    self.window.backgroundColor = [UIColor whiteColor];

    // INITIAL VIEW CONTROLLER GETS INSTANTIATED HERE 
    // (NOT BEFORE):
    MyViewController* controller = [[MyViewController alloc] init];

    self.window.rootViewController = controller;

    [self.window makeKeyAndVisible];
    return YES;
}

...但是现在,因为主故事板加载发生在幕后,视图控制器在任何应用程序之前初始化委托方法 运行.

不是寻找需要将自定义代码添加到我的初始视图控制器的解决方案。

  1. 您可以在初始 ViewController 上添加一个 UIImageView,它将包含您的应用程序的初始图像。

  2. 在 viewDidLoad()... 使 imageview.hidden 属性 False... 是否进行清理操作并在完成清理任务后使 imageview.hidden 属性 正确。

通过这种方式,用户将不知道你在做什么工作,许多公认的应用程序都使用这种方法。

我不确定是否有更优雅的方式,但肯定有一些其他方式...

I'd prefer if my initial view controller isn't even instantiated until it completes

这不是问题。您所要做的就是从 Info.plist 中删除一个 UIMainStoryboardFileNSMainNibFile 键,它告诉 UIApplicationMain 应该加载什么 UI。随后,您 运行 您在 AppDelegate 中的 "cleanup logic" 完成后,您将自己启动 UI,如示例中所示。

另一种解决方案是创建 UIApplicationMain 的子类,并在加载 UI 之前在其中进行清理 运行。

请参阅下面的应用程序生命周期

我遇到了一个非常相似的情况

我需要 运行 代码,它只在 didFinishLaunchingNotification

之后准备就绪

我想出了这个模式,它也适用于状态恢复

var finishInitToken: Any?

required init?(coder: NSCoder) {
    
    super.init(coder: coder)
    
    finishInitToken = NotificationCenter.default.addObserver(forName: UIApplication.didFinishLaunchingNotification, object: nil, queue: .main) { [weak self] (_) in
        self?.finishInitToken = nil
        self?.finishInit()
    }
    
}

func finishInit() {
    ...
}

override func decodeRestorableState(with coder: NSCoder) {
    
    // This is very important, so the finishInit() that is intended for "clean start"
    // is not called
    if let t = finishInitToken {
        NotificationCenter.default.removeObserver(t)
    }
    finishInitToken = nil
    
}

您也可以为 willFinishLaunchingWithOptions 发出通知