Swift 4中AppDelegate引用ViewController子类的机制探究
在AppDelegate中持有ViewController引用的工作原理
嘿,针对你用Swift 4遇到的这个问题,我结合你提到的应用前后台切换、文件打开这些场景,来拆解你的两个核心疑问~
问题1:什么时候需要在AppDelegate中持有ViewController引用?
AppDelegate本身是应用的“大管家”,管着生命周期事件和外部触发的事件(比如打开文件),但它不负责UI展示。所以当你需要把这些事件的信息传递给负责显示UI的ViewController时,持有它的引用就很有必要:
- 比如用户用你的App打开一个文件,AppDelegate拿到文件URL后,得把这个URL传给ViewController去解析、展示内容,这时候没有引用的话就没法直接调用ViewController的方法。
- 再比如应用从后台回到前台,你需要让ViewController刷新页面数据,也可以通过这个引用来直接触发刷新逻辑。
给你举个Swift 4的代码示例:
// 在AppDelegate里声明引用 var mainVC: ViewController? // 在应用启动时初始化ViewController并赋值 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { let mainStoryboard = UIStoryboard(name: "Main", bundle: nil) mainVC = mainStoryboard.instantiateInitialViewController() as? ViewController window?.rootViewController = mainVC window?.makeKeyAndVisible() return true } // 处理文件打开事件,调用ViewController的处理方法 func application(_ application: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { mainVC?.handleOpenedFile(url: url) return true }
(注:如果是iOS 13+苹果引入了SceneDelegate,但你用Swift 4的话大概率还是基于传统UIApplicationDelegate架构,上面的代码适配你的场景)
问题2:这种持有方式会不会导致内存泄漏,该怎么避免?
默认情况下AppDelegate是单例,生命周期和应用一样长。如果用强引用持有ViewController,当ViewController需要被销毁(比如弹出导航栈)时,因为AppDelegate还死死握着它的引用,ViewController就没法被系统回收,从而产生内存泄漏。
解决办法很简单:把AppDelegate里的引用改成弱引用(weak)。因为ViewController已经被window的rootViewController强引用了,AppDelegate只需要弱引用就能拿到实例,不会影响它的释放:
// 修改成weak引用 weak var mainVC: ViewController?
调用的时候记得用可选绑定或者可选链,确保引用不为nil:
// 可选绑定方式 if let validVC = mainVC { validVC.handleOpenedFile(url: url) } // 或者直接用可选链,和之前的例子一样 mainVC?.handleOpenedFile(url: url)
额外小技巧:解耦的替代方案
如果你不想持有引用,也可以用通知中心(NotificationCenter)来传递事件,解耦性更强:
- AppDelegate收到事件后发送通知:
NotificationCenter.default.post(name: NSNotification.Name("FileOpenedNotification"), object: url)
- ViewController里监听并处理:
override func viewDidLoad() { super.viewDidLoad() NotificationCenter.default.addObserver(self, selector: #selector(handleFileOpened(_:)), name: NSNotification.Name("FileOpenedNotification"), object: nil) } @objc private func handleFileOpened(_ notification: Notification) { guard let fileURL = notification.object as? URL else { return } // 这里处理文件逻辑 } // 记得在销毁时移除监听 deinit { NotificationCenter.default.removeObserver(self) }
内容的提问来源于stack exchange,提问作者Adam
相关产品推荐
相关产品推荐

