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

Swift代码触发SIGABRT信号崩溃排查(Core Data相关)

历史汇率获取代码崩溃(SIGABRT)排查与修复

核心崩溃原因

  1. Core Data线程安全违规
    你在URLSession.dataTask的后台线程闭包里直接操作了contextDataCore——默认情况下Core Data的上下文绑定主线程,后台线程直接读写主线程上下文会触发线程冲突,这是SIGABRT信号的主要来源。

  2. URL强制解包风险
    URL(string: url)!用了强制解包,如果currentCurrency或fromCurrency包含特殊字符(比如空格、&等),URL构造会失败,直接触发崩溃。

  3. 信号量释放路径不全
    虽然大部分分支都调用了startupCheck_ExchangeRateFromPast.leave(),但如果contextDataCore.save()抛出错误,存在遗漏释放的风险。

修复后的代码

func downloadExchangeRateFromPast(toDate: Date, fromCurrency: String) {
    startupCheck_ExchangeRateFromPast.enter()
    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = "yyyy-MM-dd"
    let fromDate = dateFormatter.string(from: toDate)
    
    // 对URL参数编码并可选绑定,避免强制解包崩溃
    guard let encodedFromCurrency = fromCurrency.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
          let encodedCurrentCurrency = currentCurrency.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
          let url = URL(string: "https://api.exchangerate.host/convert?from=\(encodedCurrentCurrency)&to=\(encodedFromCurrency)&date=\(fromDate)") else {
        startupCheck_ExchangeRateFromPast.leave()
        return
    }
    
    let session = URLSession(configuration: .default)
    
    session.dataTask(with: url) { [weak self] (data, response, _) in
        // 用defer确保所有分支都释放信号量
        defer {
            self?.startupCheck_ExchangeRateFromPast.leave()
        }
        
        guard let JSONData = data else {
            return
        }
        
        if let response = response as? HTTPURLResponse { print("Exchange Status: \(response.statusCode)") }
        
        do {
            let finalData = try JSONDecoder().decode(ExchangeRatePastJSON.self, from: JSONData)
            
            // Core Data操作切回主线程,保证线程安全
            DispatchQueue.main.async {
                guard let self = self else { return }
                let newExchangeRate = ExchangeRateConvert(context: self.contextDataCore)
                newExchangeRate.date = toDate
                newExchangeRate.fromCode = fromCurrency
                newExchangeRate.toCode = self.currentCurrency
                newExchangeRate.rateConvert = finalData.result
                
                let newExchangeHistoryRecord = ExchangeHistoryRecord(context: self.contextDataCore)
                newExchangeHistoryRecord.finalRecord = "\(toDate) - \(fromCurrency) - \(self.currentCurrency)"
                
                do {
                    try self.contextDataCore.save()
                } catch {
                    print("Core Data保存失败: \(error)")
                }
            }
        } catch {
            print("JSON解码失败: \(error)")
        }
    }
    .resume()
}

关键修复点说明

  • 线程安全:把所有Core Data实体创建和保存操作放到DispatchQueue.main.async里,确保主线程上下文只在主线程被操作;也可以根据业务需求创建私有队列上下文专门处理后台Core Data操作。
  • URL安全处理:对货币代码做URL编码,避免特殊字符导致URL构造失败,同时用可选绑定替代强制解包。
  • 信号量可靠释放:用defer语句保证无论闭包里走哪个分支,信号量都会被释放,避免死锁或资源泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 01:10:29