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

为何该RxFeedback代码未产生内存泄漏?ARC原理解析

RxFeedback中强引用self未引发内存泄漏的原因解析

首先看相关Swift代码:

ViewModel 代码

class ViewModel {
...
func state(with bindings: @escaping (Driver<State>) -> Signal<Event>) -> Driver<State> {
        Driver.system(
            initialState: .initial,
            reduce: State.reduce(state:event:),
            feedback:
                bindings,
                react(request: { $0.startLoading }, effects: { _ in
                  self.fetchFavoriteRepositoriesUseCase.execute()
                        .asObservable()
                        .observe(on: self.scheduler)
                        .map(self.repositoriesToRepositoryViewModelsMapper.map(input:))
                        .map { repositories in .loaded(repositories) }
                        .asSignal { error in
                            .just(.failed(error.localizedDescription))
                        }
            }))
    }
...
}

ViewController 代码

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        let initialTrigger = BehaviorRelay<Void>(value: ())

        let trigger = Observable.merge(initialTrigger.asObservable(), refreshRelay.asObservable())

        let uiBindings: (Driver<FavoriteRepositoriesViewModel.State>) -> Signal<FavoriteRepositoriesViewModel.Event> = bind(self) { me, state in
            let subscriptions = [
                    state.drive(onNext: { state in
                        switch state {
                        case .initial:
                            print("Initial")
                        case .loading:
                            print("Loading")
                        case .failed:
                            print("Failed")
                        case .loaded:
                            print("Loaded")
                        }
                    })
                ]

            let events: [Signal<FavoriteRepositoriesViewModel.Event>] = [
                trigger.map {
                    .load
                }
                .asSignal(onErrorSignalWith: .empty())
            ]

            return Bindings(subscriptions: subscriptions, events: events)
        }

        viewModel.state(with: uiBindings)
        .drive()
        .disposed(by: disposeBag)
    }
}

问题描述

我现在在理解为什么上述RxFeedback的react方法没有产生内存泄漏:该方法的effects参数是@escaping闭包,我没有对self做弱引用处理,而是在闭包中强引用self来调用用例。我推测这和RxFeedback无关,而是我对ARC和内存管理的理解有盲区。我通过将ViewController从导航控制器弹出的方式测试了它的释放情况,希望得到这段代码未形成引用循环的详细解释。

原因解析

要搞清楚为什么没有形成循环引用,我们先梳理整个引用关系链:

  1. ViewController 强引用 viewModel 和 disposeBag
  2. viewModel 的 state 方法中,react 的 effects 闭包强引用 viewModel(也就是闭包里的self)
  3. 这个闭包被RxFeedback的Driver.system内部的反馈逻辑持有,而Driver.system返回的Driver订阅被添加到了disposeBag中

乍一看似乎形成了ViewController -> disposeBag -> 订阅 -> Driver内部 -> effects闭包 -> viewModel -> ViewController的循环,但实际上这个环会在ViewController被弹出时被主动打破:

  • 当ViewController从导航栈弹出后,导航控制器对它的强引用会被解除。此时如果没有其他外部强引用,ARC会尝试释放ViewController。
  • ViewController的deinit方法执行时,其持有的disposeBag会被销毁,disposeBag内部的所有订阅都会被清理。
  • 订阅被销毁时,Rx框架会回收所有和该订阅关联的资源,包括Driver.system内部对effects闭包的持有引用。
  • 一旦effects闭包对viewModel的强引用被解除,viewModel的唯一强引用就来自即将被释放的ViewController,因此viewModel也会被ARC回收。

整个过程中并没有形成永久的循环引用,因为disposeBag的销毁会主动切断闭包对viewModel的引用,打破了潜在的循环。如果没有正确使用disposeBag管理订阅,那确实会引发循环引用导致内存泄漏,但这段代码里的订阅被正确绑定到了ViewController的disposeBag,所以能在ViewController释放时及时清理所有相关资源。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 10:55:20