You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于协议的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 01:55:24