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

Combine Swift中Mock DataService Publisher出现EXC_BAD_ACCESS崩溃求助

问题根源分析

崩溃的核心原因是Mock的Publisher是同步立即发送值的,而真实API的Publisher是异步的,两者执行时序的差异导致了悬垂引用:

  • ViewModel初始化过程中,访问lazy var nasaPublisher时才会创建完整的Publisher链;
  • $dataString的初始值会立即触发flatMap逻辑,此时ViewModel还未完全完成初始化;
  • 你在flatMap中使用了[unowned self],当Mock的Just同步发送值时,unowned self可能指向未完全初始化或已失效的内存地址,从而触发EXC_BAD_ACCESS。

真实API的dataTaskPublisher是异步执行的,等它发送值时ViewModel已经完成初始化,因此不会出现问题。

解决方案

方案1:替换unowned self为weak self(推荐)

unowned是无主引用,当引用对象不存在时会直接崩溃;而weak是弱引用,会自动变为nil,安全性更高。修改flatMap的捕获列表:

private lazy var nasaPublisher: AnyPublisher<String, Never> = {
    $dataString
        .flatMap { [weak self] string in
            guard let self = self else {
                return Empty().eraseToAnyPublisher()
            }
            return self.dataService.getNasaData()
        }
        .eraseToAnyPublisher()
}()

方案2:让Mock Publisher模拟异步行为

如果需要保留unowned self,可以让Mock的Publisher延迟发送值,模拟真实API的异步特性,避免在初始化过程中同步访问self:

class MockDataService: DataServiceProtocol {
    func getNasaData() -> AnyPublisher<String, Never> {
        return Just("Test string")
            .delay(for: 0.01, scheduler: DispatchQueue.main) // 轻微延迟确保ViewModel初始化完成
            .eraseToAnyPublisher()
    }
}

方案3:调整初始化逻辑

避免在ViewModel的init中直接执行assign,将订阅逻辑移到外部(比如View的onAppear方法中),确保ViewModel完全初始化后再启动Publisher链:

// ViewModel修改:移除init中的assign,添加订阅方法
class ViewModel: ObservableObject {
    @Published var dataString: String = ""
    let dataService: DataServiceProtocol
    
    init(dataService: DataServiceProtocol) {
        self.dataService = dataService
    }
    
    func subscribe() {
        nasaPublisher.assign(to: &$dataString)
    }
    
    private lazy var nasaPublisher: AnyPublisher<String, Never> = {
        $dataString
            .flatMap { [unowned self] string in
                self.dataService.getNasaData()
            }
            .eraseToAnyPublisher()
    }()
}

// 在View中调用订阅
.onAppear {
    viewModel.subscribe()
}
额外注意点
  • 你的DataService中map块存在未闭合的do语句(注释掉了catch分支),虽然不影响当前崩溃,但建议修复语法问题;
  • 使用assign(to:)时需注意内存泄漏风险,虽然@Published的assign会自动管理订阅生命周期,但结合lazy属性时仍需确保引用关系安全。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 17:50:33