弱引用Passthrough Subject无法发送数据的原因及疑问
问题:Combine中弱引用self时PassthroughSubject无法发送数据
我在使用Combine设计模式时遇到一个问题:在闭包中使用[weak self]弱引用时,PassthroughSubject无法向视图控制器发送数据,但改用强引用self就能正常接收。同时还有两个疑问:强引用是否会导致循环引用?为什么弱引用self时无法发送数据?
相关代码
弱引用self的ViewModel实现
import Foundation import Combine class ViewModel { var publisher = PassthroughSubject<[WebData], Never>() let apis : [ApiProtocol] init(apis: [ApiProtocol]){ self.apis = apis getData() } func getData(){ guard !apis.isEmpty else { return } apis.forEach {api in api.getApiData { [weak self] webData in self?.publisher.send(webData) } } } }
强引用self的ViewModel实现
func getData(){ guard !apis.isEmpty else { return } apis.forEach {api in api.getApiData { webData in self.publisher.send(webData) } } }
视图控制器DataVC代码
class DataVC: UIViewController { weak var viewModel : ViewModel? var models = [WebData]() var cancellable = [AnyCancellable]() @IBOutlet weak var tableView: UITableView! override func viewDidLoad() { super.viewDidLoad() downloadData() tableView.delegate = self tableView.dataSource = self } func downloadData() { guard let model = viewModel else { return } model.publisher .receive(on: DispatchQueue.main) .sink { _ in } receiveValue: { [weak self] value in self?.models.append(contentsOf: value) DispatchQueue.main.async { self?.tableView.reloadData() } }.store(in: &cancellable) } }
补充说明
将DataVC中viewModel的声明从weak var viewModel : ViewModel?改为非弱引用的var viewModel : ViewModel!后,弱引用self的ViewModel代码可正常工作,但疑惑的是:为什么viewModel为弱引用时,即使通过guard-let解包后使用,仍无法正常工作?
问题分析与解答
1. 强引用self是否会导致循环引用?
不会。分析引用链:
- ViewModel持有
apis数组(强引用) - 每个Api实例持有回调闭包(强引用),闭包持有ViewModel的强引用
但ViewModel并不持有这个回调闭包,当Api的异步请求完成后,Api实例会被释放,对应的闭包也会被释放,不会形成闭环的循环引用。不过从最佳实践来说,还是推荐使用[weak self]避免潜在的内存问题。
2. 弱引用self时无法发送数据的核心原因
问题出在视图控制器中对ViewModel的弱引用导致ViewModel提前被销毁:
- 当DataVC中声明
weak var viewModel: ViewModel?,如果ViewModel没有其他强引用持有者,那么在viewDidLoad执行完毕后,ARC会立即释放ViewModel实例。 - 虽然
downloadData里用guard let model = viewModel解包得到了ViewModel的引用,但这只是临时的强引用,方法执行完毕后这个引用就会消失,ViewModel还是会被销毁。 - 当Api的异步回调触发时,
[weak self]中的self已经是nil,self?.publisher.send(webData)不会执行,自然视图控制器收不到数据。
3. 改为非弱引用viewModel后正常的原因
当DataVC持有ViewModel的强引用(var viewModel: ViewModel!),ViewModel实例的生命周期会和DataVC绑定,不会被提前释放。Api异步回调触发时,[weak self]中的self仍然指向有效的ViewModel实例,publisher.send(webData)能正常执行,数据可以顺利发送到视图控制器的sink闭包中。
内容的提问来源于stack exchange,提问作者incelemeTRe
相关产品推荐
相关产品推荐

