基于协议的ViewModel问题:子类控制器无法访问专属成员
解决基于协议的ViewModel子类专属成员访问问题
问题本质很明确:你在BaseViewController里把viewModel声明为BaseViewModelProtocol类型,但这个协议只定义了共享的name属性和reset()方法,子类ViewModel1/ViewModel2的专属成员(比如score、something())并没有被协议包含,所以编译器无法识别这些成员,报错是必然的。
下面给你几种实用的解决思路:
方案1:子类中显式类型转换
在子类控制器里,把viewModel强制转换为对应的子类ViewModel类型,就能访问专属成员了。这种方式简单直接,适合快速修复:
class ViewController1: BaseViewController { override func viewDidLoad() { super.viewDidLoad() // 转换为ViewModel1类型后访问score if let viewModel1 = viewModel as? ViewModel1 { print(viewModel1.score) viewModel1.someMethods() } } } class ViewController2: BaseViewController { override func viewDidLoad() { super.viewDidLoad() // 转换为ViewModel2类型后访问money和something() (viewModel as? ViewModel2)?.something() print((viewModel as? ViewModel2)?.money) } }
优点:改动最小,不需要修改基类;缺点:每次访问都要做类型转换,代码略显繁琐,且如果类型不匹配会得到nil,需要处理可选值。
方案2:用泛型重构基类(推荐)
给BaseViewController添加泛型约束,让每个子类控制器可以指定自己对应的ViewModel类型,这样既能复用基类逻辑,又能直接访问子类专属成员,代码更优雅:
// 修改BaseViewController为泛型类 class BaseViewController<VM: BaseViewModelProtocol>: UIViewController { var viewModel: VM init(viewModel: VM) { self.viewModel = viewModel super.init(nibName: nil, bundle: nil) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } // 子类控制器指定对应的ViewModel类型 class ViewController1: BaseViewController<ViewModel1> { override func viewDidLoad() { super.viewDidLoad() // 直接访问ViewModel1的专属成员,无需转换 print(viewModel.score) viewModel.someMethods() } } class ViewController2: BaseViewController<ViewModel2> { override func viewDidLoad() { super.viewDidLoad() viewModel.something() print(viewModel.money) } }
优点:类型安全,无需手动转换,代码简洁;缺点:需要修改基类为泛型,但这是符合Swift类型系统设计的最佳实践。
方案3:定义子协议(适合有共享专属成员的场景)
如果多个子类ViewModel有部分共同的专属成员,可以定义继承自BaseViewModelProtocol的子协议,然后在对应控制器里使用子协议类型:
// 为ViewModel1定义子协议 protocol ViewModel1Protocol: BaseViewModelProtocol { var score: Int { get set } func someMethods() } // 让ViewModel1遵守子协议 class ViewModel1: BaseViewModel, ViewModel1Protocol { var score: Int = 0 func someMethods() {} } // 修改ViewController1的viewModel类型 class ViewController1: BaseViewController { // 直接声明为子协议类型 var viewModel: ViewModel1Protocol? init(viewModel: ViewModel1Protocol) { self.viewModel = viewModel super.init(viewModel: viewModel) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { super.viewDidLoad() print(viewModel?.score) viewModel?.someMethods() } }
优点:可以为一类ViewModel抽象出更具体的接口;缺点:如果每个子类ViewModel的专属成员都不一样,这种方式会产生大量子协议,反而增加复杂度,所以只适合有共性的子类场景。
内容的提问来源于stack exchange,提问作者iRogerz
相关产品推荐
相关产品推荐

