UIViewController与UITableViewDelegate间通信的最优实现方案咨询
最优实现方案分析
这是iOS开发中很常见的组件解耦场景,直接给数据源类加VC的弱引用虽然能解决问题,但并不是最优解——我们有更优雅、低耦合的方式来处理这个需求,下面给你梳理几个常用方案:
方案一:闭包回调(推荐,轻量场景首选)
这种方式最简洁,而且能完美避免循环引用问题。你可以在你的UITableViewDataSource(兼Delegate)类里定义一个闭包属性,用来传递选中事件:
class CustomDataSource: NSObject, UITableViewDataSource, UITableViewDelegate { // 定义回调闭包,参数可以根据需求调整(比如传模型、indexPath等) var didSelectRow: ((IndexPath) -> Void)? // 实现didSelectRowAtIndexPath func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { // 触发闭包 didSelectRow?(indexPath) } // 其他数据源方法... }
然后在你的UIViewController里,初始化数据源的时候给这个闭包赋值,注意用[weak self]避免循环引用:
class YourViewController: UIViewController { var tableView: UITableView! var dataSource1: CustomDataSource! var dataSource2: CustomDataSource! override func viewDidLoad() { super.viewDidLoad() // 初始化数据源1 dataSource1 = CustomDataSource() dataSource1.didSelectRow = { [weak self] indexPath in guard let self = self else { return } // 在这里执行segue或者弹窗 self.performSegue(withIdentifier: "YourSegueID", sender: indexPath) // 或者显示弹窗 let alert = UIAlertController(title: "提示", message: "选中了第\(indexPath.row)行", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "确定", style: .default)) self.present(alert, animated: true) } // 数据源2同理... dataSource2 = CustomDataSource() dataSource2.didSelectRow = { [weak self] indexPath in // 处理另一种数据源的选中逻辑 } } // 根据条件切换数据源 func switchDataSource(_ condition: Bool) { tableView.dataSource = condition ? dataSource1 : dataSource2 tableView.delegate = condition ? dataSource1 : dataSource2 tableView.reloadData() } }
方案二:自定义代理协议(适合多回调场景)
如果你的数据源需要传递多种事件(不止选中),自定义代理会更规范,可读性更强。
首先定义一个代理协议:
protocol DataSourceSelectionDelegate: AnyObject { func dataSource(_ dataSource: CustomDataSource, didSelectRowAt indexPath: IndexPath) // 可以添加更多回调方法,比如didDeselectRow、didScroll等 }
然后在数据源类里添加弱引用的代理属性:
class CustomDataSource: NSObject, UITableViewDataSource, UITableViewDelegate { weak var delegate: DataSourceSelectionDelegate? func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { delegate?.dataSource(self, didSelectRowAt: indexPath) } // 其他数据源方法... }
让你的UIViewController遵守这个协议并实现方法:
class YourViewController: UIViewController, DataSourceSelectionDelegate { var tableView: UITableView! var dataSource1: CustomDataSource! var dataSource2: CustomDataSource! override func viewDidLoad() { super.viewDidLoad() dataSource1 = CustomDataSource() dataSource1.delegate = self dataSource2 = CustomDataSource() dataSource2.delegate = self } // 实现代理方法 func dataSource(_ dataSource: CustomDataSource, didSelectRowAt indexPath: IndexPath) { // 可以区分是哪个数据源触发的事件 if dataSource === dataSource1 { // 处理数据源1的选中逻辑 performSegue(withIdentifier: "SegueFromSource1", sender: indexPath) } else if dataSource === dataSource2 { // 处理数据源2的选中逻辑 let alert = UIAlertController(title: "来自数据源2", message: "选中行:\(indexPath.row)", preferredStyle: .alert) present(alert, animated: true) } } // 切换数据源逻辑... }
不推荐的方案:直接弱引用UIViewController
虽然给数据源类加weak var viewController: YourViewController?也能实现需求,但这种方式会让数据源和VC的耦合度变高——数据源不应该知道VC的存在,它只需要负责提供数据和传递事件即可。一旦VC的结构变化,数据源也需要修改,维护成本更高。
总结
- 轻量场景(只有选中回调)优先用闭包,代码简洁、解耦性好;
- 多回调场景优先用自定义代理,结构清晰、扩展性强;
- 无论用哪种方式,都要注意使用弱引用(闭包用
[weak self],代理属性加weak),避免循环引用导致内存泄漏。
内容的提问来源于stack exchange,提问作者ed'
相关产品推荐
相关产品推荐

