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

不使用Storyboard(无Segues)时Swift中ViewController间传数据及代理实现

嘿,我完全懂你现在的纠结——不用Storyboard的时候,ViewController之间传数据确实没Segue那么顺手,单例又褒贬不一,代理协议听起来靠谱但不知道怎么落地对吧?我来给你把这些理清楚,尤其是代理的实现步骤,再补充几个实用的替代方案。

先说说为什么单例传数据要谨慎

单例的问题在于它是全局共享的,一旦用它传业务数据,很容易让各个ViewController之间产生强耦合——比如多个页面都修改单例里的数据,你很难追踪到底是谁改的,调试起来头大。而且单例会一直驻留在内存里,如果是大数据对象,对内存也不友好。除非是全局配置这种几乎不会变的东西,不然传业务数据真的不推荐用单例。

不用Storyboard时,ViewController间数据传递的靠谱方案

1. 正向传数据:直接初始化传参(最简单安全)

如果是从A页面跳转到B页面,需要把数据从A传给B,根本不用复杂的东西——直接给B写个带参数的初始化方法就行。

比如B页面的代码:

class DetailViewController: UIViewController {
    // 要接收的数据模型
    private let item: YourDataModel
    
    // 自定义初始化方法
    init(with item: YourDataModel) {
        self.item = item
        super.init(nibName: nil, bundle: nil) // 纯代码初始化的话,nibName传nil
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented") // 不用Storyboard的话,这个可以直接报错
    }
    
    // 后续就可以直接用self.item了
}

然后在A页面里创建B并跳转:

class ListViewController: UIViewController {
    // 假设这里有个选中的数据项
    private var selectedItem: YourDataModel!
    
    private func goToDetailPage() {
        let detailVC = DetailViewController(with: selectedItem)
        navigationController?.pushViewController(detailVC, animated: true)
    }
}

这种方式最直接,数据流向清晰,而且完全是类型安全的,编译器会帮你检查参数是否正确,不会出现传错数据的情况。

2. 反向传数据:用代理协议(适合复杂交互)

如果是B页面操作完数据,要把结果传回A页面,这时候代理协议就是标准解决方案了。步骤很清晰:

第一步:在B页面定义代理协议

// 定义协议,用AnyObject限制只能类类型遵守(避免结构体)
protocol DetailViewControllerDelegate: AnyObject {
    // 声明传递数据的方法,把当前VC和数据都传回去,方便A页面识别来源
    func detailViewController(_ vc: DetailViewController, didFinishUpdating item: YourDataModel)
    // 可选:如果需要处理取消操作,再加一个方法
    func detailViewControllerDidCancel(_ vc: DetailViewController)
}

第二步:在B页面添加弱引用的代理属性

一定要用weak,不然会导致A和B互相持有,产生内存泄漏:

class DetailViewController: UIViewController {
    weak var delegate: DetailViewControllerDelegate?
    private var currentItem: YourDataModel!
    
    // 自定义初始化方法接收初始数据
    init(with item: YourDataModel) {
        self.currentItem = item
        super.init(nibName: nil, bundle: nil)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    // 比如用户点击保存按钮时,调用代理方法
    @objc private func saveTapped() {
        // 假设已经修改了currentItem
        delegate?.detailViewController(self, didFinishUpdating: currentItem)
        // 关闭页面
        navigationController?.popViewController(animated: true)
    }
    
    @objc private func cancelTapped() {
        delegate?.detailViewControllerDidCancel(self)
        navigationController?.popViewController(animated: true)
    }
}

第三步:在A页面遵守协议并设置代理

class ListViewController: UIViewController, DetailViewControllerDelegate {
    // ...
    
    private func goToDetailPage() {
        let detailVC = DetailViewController(with: selectedItem)
        detailVC.delegate = self // 设置代理
        navigationController?.pushViewController(detailVC, animated: true)
    }
    
    // 实现代理方法
    func detailViewController(_ vc: DetailViewController, didFinishUpdating item: YourDataModel) {
        // 在这里处理传回的数据,比如更新列表
        updateList(with: item)
    }
    
    func detailViewControllerDidCancel(_ vc: DetailViewController) {
        // 处理取消操作,比如刷新状态或者什么都不做
    }
}

3. 反向传数据:用闭包(轻量简洁)

如果只是简单的回调,比如只需要传一个数据或者通知取消,闭包比代理更简洁,不用写协议和遵守方法。

在B页面定义闭包属性

class DetailViewController: UIViewController {
    private var currentItem: YourDataModel!
    // 定义闭包,可选类型,避免强制解包
    var onUpdate: ((YourDataModel) -> Void)?
    var onCancel: (() -> Void)?
    
    init(with item: YourDataModel) {
        self.currentItem = item
        super.init(nibName: nil, bundle: nil)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    @objc private func saveTapped() {
        onUpdate?(currentItem) // 调用闭包传数据
        navigationController?.popViewController(animated: true)
    }
    
    @objc private func cancelTapped() {
        onCancel?() // 调用取消闭包
        navigationController?.popViewController(animated: true)
    }
}

在A页面创建B时设置闭包

注意要用[weak self]避免循环引用:

class ListViewController: UIViewController {
    // ...
    
    private func goToDetailPage() {
        let detailVC = DetailViewController(with: selectedItem)
        // 设置更新闭包
        detailVC.onUpdate = { [weak self] updatedItem in
            guard let self = self else { return }
            self.updateList(with: updatedItem)
        }
        // 设置取消闭包
        detailVC.onCancel = {
            print("用户取消了操作")
        }
        navigationController?.pushViewController(detailVC, animated: true)
    }
}
总结一下
  • 正向传数据:优先用自定义初始化传参,类型安全,逻辑清晰。
  • 反向传数据:复杂交互用代理协议,简单场景用闭包,两者都是安全可控的,不会有单例的耦合问题。
  • 单例尽量只用来存全局配置类的静态数据,别用来传业务数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 09:07:21