UITableViewCell的Publisher多次发送值的问题怎么解决?
问题根因
UITableViewCell存在复用机制,你每次在cellForRowAt中给复用的Cell的Publisher新增订阅时,旧的订阅没有被取消,同一个Cell会被绑定多个订阅,点击按钮时所有绑定的订阅都会触发,就出现了多次回调的问题。
最优解决方案:Cell内部管理订阅生命周期
我们只需要让每个Cell自己管理订阅的生命周期,在Cell即将被复用的时候自动清空之前的订阅即可,不需要用到特殊的Operator,符合UITableView的复用逻辑:
- 修改自定义Cell代码,新增自己的Cancellable集合,重写重用方法清空订阅
import UIKit import Combine class PostTVCell: UITableViewCell { @IBOutlet private var lblTitle: UILabel! private var postModel: PostModel? public var showDetailsPublisher = PassthroughSubject<PostModel, Never>() // 新增Cell持有私有Cancellable集合 private var cancellables = Set<AnyCancellable>() public func load(with model: PostModel) { self.postModel = model lblTitle.text = model.title } @IBAction private func showDetails(_ sender: UIButton) { guard let postModel = postModel else { return } showDetailsPublisher.send(postModel) } // 重写重用生命周期方法,清空所有旧订阅 override func prepareForReuse() { super.prepareForReuse() cancellables.removeAll() } }
- 修改ViewController中cellForRowAt的订阅存储逻辑,把订阅存在Cell自己的Cancellable集合中,不要存在ViewController全局的subscriptions里
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! PostTVCell let post = postList[indexPath.row] cell.load(with: post) cell.showDetailsPublisher .sink { postModel in print("Closure called") } // 存储到对应Cell自己的集合中 .store(in: &cell.cancellables) return cell }
临时替代方案
如果你不想修改Cell的代码,也可以用系统自带的prefix(1)操作符,让每个订阅只接收一次事件就自动失效,不过这种方案会产生冗余订阅,内存占用更高,仅做参考:
cell.showDetailsPublisher .prefix(1) .sink { postModel in print("Closure called") } .store(in: &subscriptions)
内容的提问来源于stack exchange,提问作者Kazi Abdullah Al Mamun
相关产品推荐
相关产品推荐

