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

如何在UIKit中将@Published对象作为函数参数传递?

UIKit下Combine订阅外部@Published对象的解决方案

核心问题分析

原代码的问题在于:

  • searchTextManipulation 函数接收的是 String 类型参数,但内部订阅的是 ViewModel 自身的 $searchText,传入的字符串并未参与到订阅流中,导致 sink 中始终获取的是 ViewModel 初始的空字符串。
  • 要实现外部 @Published 对象的订阅,需要让 ViewModel 接收Publisher 类型的参数,而非单纯的字符串值。

可行方案

方案一:让ViewModel接收外部Publisher

直接将外部的 @Published 属性对应的 Publisher 传入 ViewModel,由 ViewModel 订阅该流并执行搜索逻辑。

修改MainViewModel:

final class MainViewModel {
    @Published var searchText = ""
    // 存储订阅对象,避免内存泄漏
    private var cancellables = Set<AnyCancellable>()

    /// 订阅外部传入的搜索文本Publisher
    /// - Parameter searchPublisher: 外部的String类型Publisher
    func setupSearchSubscription(with searchPublisher: AnyPublisher<String, Never>) {
        searchPublisher
            .debounce(for: .seconds(1.0), scheduler: RunLoop.main)
            .removeDuplicates()
            .sink { [weak self] inputText in
                // 可选:同步外部文本到ViewModel自身的searchText
                self?.searchText = inputText
                // 执行你的搜索逻辑
                print("开始搜索:\(inputText)")
            }
            .store(in: &cancellables)
    }
}

在MainViewController中调用:

final class MainViewController: UIViewController {
    let viewModel = MainViewModel()
    @Published var searchText = ""
    private var cancellables = Set<AnyCancellable>()

    override func viewDidLoad() {
        super.viewDidLoad()
        // 将ViewController的searchText Publisher传入ViewModel
        viewModel.setupSearchSubscription(with: $searchText.eraseToAnyPublisher())
        
        // 测试示例:模拟用户输入
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
            self.searchText = "UIKit Combine 实战"
        }
    }
}

方案二:双向绑定外部与ViewModel的searchText

如果希望保持 ViewModel 的 searchText 作为唯一数据源,可以将 ViewController 的 searchText 与 ViewModel 的 searchText 双向绑定,ViewModel 订阅自身的 $searchText 即可。

修改MainViewModel:

final class MainViewModel {
    @Published var searchText = ""
    private var cancellables = Set<AnyCancellable>()

    /// 订阅自身的searchText变化
    func setupInternalSearch() {
        $searchText
            .debounce(for: .seconds(1.0), scheduler: RunLoop.main)
            .removeDuplicates()
            .sink { text in
                // 执行搜索逻辑
                print("开始搜索:\(text)")
            }
            .store(in: &cancellables)
    }
}

在MainViewController中绑定:

final class MainViewController: UIViewController {
    let viewModel = MainViewModel()
    @Published var searchText = ""
    private var cancellables = Set<AnyCancellable>()

    override func viewDidLoad() {
        super.viewDidLoad()
        // 双向绑定:ViewController的searchText变化同步到ViewModel
        $searchText
            .assign(to: \.searchText, on: viewModel)
            .store(in: &cancellables)
        
        // 启动ViewModel内部的搜索订阅
        viewModel.setupInternalSearch()
        
        // 测试示例
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
            self.searchText = "UIKit 搜索优化"
        }
    }
}

注意事项

  • 必须用 Set<AnyCancellable> 存储订阅对象,否则订阅会立即失效。
  • 使用 [weak self] 避免 ViewModel 被循环引用。
  • eraseToAnyPublisher() 用于将具体的 Published.Publisher 类型转为通用的 AnyPublisher,降低耦合度。

内容的提问来源于stack exchange,提问作者Emre Değirmenci

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 23:45:43