Swift MVVM使用协议处理ViewModel事件报错解决方案咨询
解决MVVM代理模式中的初始化类型不匹配问题
这个问题我之前开发MVVM的时候也踩过坑!咱们先搞清楚报错的根源:你在SecondViewController的属性定义里直接写let viewModel = SecondViewModel(del: self),这时候ViewController的实例还没有完成初始化流程,self在这里的类型并不是SecondViewController实例,而是一个构造闭包类型(SecondViewController) -> () -> SecondViewController,自然不符合SecondViewModelEvents协议的要求,所以才会抛出类型不匹配的错误。
下面给你两种常用的正确实现方式:
方式一:使用lazy var延迟初始化ViewModel
lazy属性的特点是第一次被访问时才会初始化,这时候ViewController已经完成实例化,self就是合法的协议遵守者实例了:
class SecondViewController: UIViewController, SecondViewModelEvents { // 用lazy var替代let,确保初始化时self已完全创建 lazy var viewModel = SecondViewModel(del: self) @IBAction func buttonPressed(_ sender: Any) { self.viewModel.loadDataFromServer() } func changeBackground() { self.view.backgroundColor = UIColor.red } }
方式二:在viewDidLoad中初始化ViewModel
ViewController的viewDidLoad方法会在视图加载完成后调用,这时候实例已经完全初始化,self的类型完全符合要求:
class SecondViewController: UIViewController, SecondViewModelEvents { // 先声明为隐式解包可选类型,后续赋值 var viewModel: SecondViewModel! override func viewDidLoad() { super.viewDidLoad() // 这里self已经是合法的SecondViewController实例 viewModel = SecondViewModel(del: self) } @IBAction func buttonPressed(_ sender: Any) { self.viewModel.loadDataFromServer() } func changeBackground() { self.view.backgroundColor = UIColor.red } }
额外优化:避免循环引用
这里还要提醒你一个容易忽略的点:当前的代码存在循环引用风险——SecondViewController持有viewModel,而viewModel的events属性又持有SecondViewController,会导致两者都无法被释放。解决方法是给协议添加类约束,并把events声明为weak:
// 给协议添加AnyObject约束,确保只有类能遵守 protocol SecondViewModelEvents: AnyObject { func changeBackground() } class SecondViewModel: NSObject { // 用weak修饰代理属性,打破循环引用 weak var events: SecondViewModelEvents? init(del: SecondViewModelEvents) { self.events = del } func loadDataFromServer() { self.events?.changeBackground() } }
这样整个MVVM代理模式的实现就既正确又安全啦!
内容的提问来源于stack exchange,提问作者yildirimatcioglu
相关产品推荐
相关产品推荐

