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

Swift如何绑定数据?RxSwift+MVVM传递产品ID索引越界如何解决

问题修复方案

现有问题汇总

  • 数组越界错误:products数组的实际元素数量和UITableView展示的行数不一致,点击时访问了不存在的下标
  • 代码笔误:跳转时变量名写错、ViewModel里仓库实例名和错误回调变量名不匹配
  • ID传递时序错误:DetailViewController的ViewModel初始化早于ID赋值,导致请求时拿到的是默认ID而非传递的正确值
  • 强制解包风险:接口返回数据处用!强制解包,空数据时会直接闪退

具体修改步骤

1. 修复SelectViewController跳转逻辑

先加下标安全校验,修正跳转变量名,保证赋值顺序正确:

extension SelectViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
        // 下标安全校验,避免越界
        guard indexPath.row < products.count else { return }
        let detailVC = DetailViewController(productId: products[indexPath.row].id)
        detailVC.product = products[indexPath.row]
        // 修正笔误:之前写的detailPassVC改为实际创建的detailVC
        self.navigationController?.pushViewController(detailVC, animated: true)
    }
}

同时确认产品列表数据请求成功、更新products数组后,必须调用tableView.reloadData()刷新列表,保证UITableView的行数和products.count完全一致。

2. 修复DetailViewController初始化逻辑

新增自定义构造器强制传递产品ID,保证ViewModel初始化时ID已经赋值完成:

class DetailViewController: UIViewController {
    var product = ProductModel()
    // 改为必传的隐式解包,不设置默认值
    var productId: Int!
    private let disposeBag = DisposeBag()
    private let viewModel = DetailViewModel()
    
    // 自定义构造器强制传入产品ID
    init(productId: Int) {
        self.productId = productId
        super.init(nibName: nil, bundle: nil)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 此时productId已经赋值完成,再初始化ViewModel
        initViewModel()
    }
    
    func initViewModel() {
        let input = DetailViewModel.Input(loadDetailData: getData.asSignal(onErrorJustReturn: ()), id: productId)
        let output = viewModel.transform(input: input)
        output.loadDetail.asObservable()
            .bind(onNext: { [weak self] data in
                guard let self = self, let data = data else { return }
                // 替换强制解包为安全可选绑定
                self.infoLabel.text = data.detailDescription
                self.passView.setData(data)
                self.secondView.setData(data)
                self.fareView.setData(data)
                self.totalLabel.text = totalPrice(data)
            }).disposed(by: disposeBag)
    }
}

3. 修复DetailViewModel代码笔误

修正变量名不匹配的问题,添加弱引用避免循环引用:

class DetailViewModel: BaseViewModel {
    private let repo = ProductRepository()
    private let disposeBag = DisposeBag()
        
    struct Input {
        let loadDetailData: Signal<Void>
        let id: Int
    }
    struct Output {
        let loadDetail: Driver<DetailModel?>
        let error: Driver<String?>
    }
    
    func transform(input: Input) -> Output {
        let loadDetail = PublishSubject<DetailModel?>()
        let msg = PublishSubject<String?>()
        
        input.loadDetailData.asObservable()
            .flatMap { [weak self] _ in
                guard let self = self else { return Observable.empty() }
                // 修正变量名:之前写的productRepo改为实际声明的repo
                return self.repo.detailProduct(input.id)
            }
            .subscribe(onNext: { data in
                switch data {
                case let .success(detail):
                    loadDetail.onNext(detail)
                default:
                    // 修正变量名:之前写的errorMessage改为实际声明的msg
                    msg.onNext("load fail")
                }
            }).disposed(by: disposeBag)
        
        return Output(
            loadDetail: loadDetail.asDriver(onErrorJustReturn: nil),
            error: msg.asDriver(onErrorJustReturn: nil)
        )
    }
}

额外注意事项

  • 所有异步回调闭包都要加[weak self]避免循环引用导致内存泄漏
  • 不要对网络返回的可选值做强制解包,全部用guard let/if let做安全校验
  • 如果产品列表有分页加载、删除等操作,每次更新products数组后都必须刷新列表,保证数据源和UI同步

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 23:15:02