打开SettingsViewController时UIButton置空致APP崩溃,如何解决?
解决Present SettingsViewController后调用MainViewController方法导致Outlet为nil的崩溃问题
首先咱们先搞清楚崩溃的核心原因:你在SettingsViewController里调用mainViewController.setZoomTrue()/setZoomFalse()时,这个mainViewController大概率不是当前正在显示的那个实例——要么是你新创建了一个MainViewController对象(它的Outlet根本没经过Storyboard/XIB的初始化,自然是nil),要么是引用方式不对导致原实例被释放了,所以调用方法时访问到nil的Outlet就崩溃了。
下面给你两种可行的解决办法,优先推荐第一种,因为更符合iOS开发的解耦思路:
方法一:用闭包(Closure)传递状态(推荐)
这种方式不需要让SettingsViewController持有MainViewController的引用,而是通过闭包把开关状态传递回去,既解耦又避免循环引用问题。
- 在SettingsViewController中定义闭包属性
class SettingsViewController: UIViewController { // 定义一个闭包,用来传递开关的状态 var zoomSwitchChangedHandler: ((Bool) -> Void)? // 你的其他代码... }
- 在MainViewController present时绑定闭包
当你从MainViewController打开SettingsVC的时候,把处理开关状态的逻辑通过闭包传进去:
// 假设你是这样获取SettingsViewController实例的,根据你的实际代码调整 let settingsVC = self.settingsViewController settingsVC.zoomSwitchChangedHandler = { [weak self] isOn in guard let self = self else { return } // 直接在这里处理按钮显隐 if isOn { self.setZoomTrue() } else { self.setZoomFalse() } } self.present(settingsVC, animated: true, completion: nil)
这里的[weak self]很重要,能避免循环引用,防止MainViewController因为被SettingsVC持有而无法正常释放。
- 在SettingsViewController的开关回调中调用闭包
修改你的switchChanged1方法,不用直接调用MainVC的方法,而是触发闭包:
@objc func switchChanged1(_ sender : UISwitch!){ let defaults = UserDefaults.standard defaults.set(sender.isOn, forKey: settingsView.settingsIsZoom) isZoom = sender.isOn // 调用闭包,把状态传回去 zoomSwitchChangedHandler?(isZoom) }
方法二:正确持有MainViewController的弱引用
如果你一定要保持直接调用MainVC方法的方式,那要确保引用的是当前显示的实例,并且用弱引用避免循环引用:
- 在SettingsViewController中定义弱引用属性
class SettingsViewController: UIViewController { // 用weak修饰,避免循环引用 weak var mainViewController: MainViewController? // 你的其他代码... }
- 在MainViewController present时传递自身引用
self.settingsViewController.mainViewController = self self.present(self.settingsViewController, animated: true, completion: nil)
- 在开关回调中先判断引用是否存在
修改switchChanged1方法,先确认mainViewController没有被释放,再调用方法:
@objc func switchChanged1(_ sender : UISwitch!){ let defaults = UserDefaults.standard defaults.set(sender.isOn, forKey: settingsView.settingsIsZoom) isZoom = sender.isOn // 先判断mainViewController是否存在,避免nil崩溃 guard let mainVC = mainViewController else { return } if isZoom { mainVC.setZoomTrue() } else { mainVC.setZoomFalse() } }
总结
优先选择闭包的方式,因为它让两个ViewController之间的依赖关系更清晰,也能减少内存泄漏的风险。而直接持有引用的方式虽然简单,但会增加代码耦合,后期维护起来容易出问题。
内容的提问来源于stack exchange,提问作者kacerac
相关产品推荐
相关产品推荐

