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

使用装配共享容器:如何向Coordinator与Controller传递同一对象

解决Coordinator与ViewController共享同一ViewModel实例的问题

这个问题我在做RxSwift项目时也踩过坑——核心就是要打破“每次解析都新建实例”的现状,让Coordinator和ViewController拿到完全相同的ViewModel对象。下面给你几个落地性强的方案:

方案1:由Coordinator统一创建并传递ViewModel

Coordinator本来就负责页面导航和对象生命周期管理,把ViewModel的创建权收归它最合适:

  • 第一步:在Coordinator内部初始化ViewModel实例,自己持有这个引用
  • 第二步:初始化ViewController时,把这个已创建好的ViewModel实例注入进去
  • 第三步:Coordinator自身如果需要使用ViewModel,直接用自己持有的这个实例就行

举个Swift代码示例:

class LSCoordinator: Coordinator {
    private let viewModel: LSViewModel = LSViewModel() // 只初始化一次
    private let disposeBag = DisposeBag()
    
    func start() {
        let vc = LSViewController(viewModel: viewModel) // 传递同一个实例
        // 导航到vc的逻辑...
        
        // Coordinator自己操作ViewModel
        viewModel.someSubject.subscribe(onNext: { value in
            print("Coordinator收到值:\(value)")
        }).disposed(by: disposeBag)
    }
}

// ViewController通过初始化方法接收ViewModel
class LSViewController: UIViewController {
    private let viewModel: LSViewModel
    private let disposeBag = DisposeBag()
    
    init(viewModel: LSViewModel) {
        self.viewModel = viewModel
        super.init(nibName: nil, bundle: nil)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 这里的viewModel和Coordinator的是同一个,Rx绑定正常工作
        viewModel.someSubject.subscribe(onNext: { value in
            print("ViewController收到值:\(value)")
        }).disposed(by: disposeBag)
    }
}

方案2:用依赖注入容器统一管理实例

如果项目规模较大,页面和ViewModel较多,推荐用依赖注入(DI)容器来统一控制ViewModel的创建:

  • 定义一个DI容器类,注册ViewModel时指定为单例模式或者每次解析返回同一实例
  • Coordinator和ViewController都从容器中获取ViewModel,而不是自己初始化

示例代码:

// 简单的DI容器实现
class DIContainer {
    static let shared = DIContainer()
    private var registrations: [String: Any] = [:]
    
    func register<T>(_ type: T.Type, instance: T) {
        let key = String(describing: type)
        registrations[key] = instance
    }
    
    func resolve<T>(_ type: T.Type) -> T? {
        let key = String(describing: type)
        return registrations[key] as? T
    }
}

// 全局初始化时注册ViewModel实例
DIContainer.shared.register(LSViewModel.self, instance: LSViewModel())

// Coordinator中获取实例
class LSCoordinator: Coordinator {
    private let viewModel: LSViewModel? = DIContainer.shared.resolve(LSViewModel.self)
    private let disposeBag = DisposeBag()
    
    func start() {
        guard let viewModel = viewModel else { return }
        let vc = LSViewController(viewModel: viewModel)
        // 导航逻辑...
    }
}

方案3:ViewModel设为单例(谨慎使用)

这是最简单的方案,但要注意适用场景——仅当这个ViewModel是全局唯一、不随页面销毁而重置时才用:

class LSViewModel {
    static let shared = LSViewModel() // 全局单例
    private init() {} // 禁止外部初始化
    
    // Rx相关序列定义
    let someSubject = PublishSubject<String>()
}

// Coordinator和ViewController直接使用单例
class LSCoordinator: Coordinator {
    private let disposeBag = DisposeBag()
    
    func start() {
        let vc = LSViewController()
        LSViewModel.shared.someSubject.subscribe(onNext: { value in
            print("Coordinator收到值:\(value)")
        }).disposed(by: disposeBag)
    }
}

class LSViewController: UIViewController {
    private let disposeBag = DisposeBag()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        LSViewModel.shared.someSubject.subscribe(onNext: { value in
            print("ViewController收到值:\(value)")
        }).disposed(by: disposeBag)
    }
}

⚠️ 注意:如果这个ViewModel是页面专属的,用单例会导致页面关闭后状态残留,下次进入页面时拿到旧状态,容易引发bug。

关键提醒

你遇到的Rx绑定失效、打印不执行,本质就是两个ViewModel实例各自维护独立的Rx序列——只要保证Coordinator和ViewController用的是同一个实例,所有绑定都会正常工作。检查一下之前的代码,是不是在两个地方分别写了let viewModel = LSViewModel(),把这两处改成共享同一个实例就解决了。

内容的提问来源于stack exchange,提问作者A_G

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 07:31:31