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

SwiftData异步导入数据时随机触发EXC_BAD_ACCESS问题求助

SwiftData异步导入交易数据时随机触发EXC_BAD_ACCESS问题排查

问题概述

使用SwiftData构建一对多关系(Transaction与Category),异步导入文本文件中的交易数据时,随机触发EXC_BAD_ACCESS错误。崩溃位置固定在设置Transaction的category属性时,但崩溃时机和涉及的交易数据不固定。已使用async/await异步加载数据,通过MainActor更新进度条,借助actor插入交易数据;尝试过Address Sanitizer、Thread Sanitizer和Zombie objects工具调试,未定位到问题根源,怀疑是关系设置错误、数据竞争或内存问题。

相关代码

模型定义代码

@Model
class Transaction: Hashable {
    @Attribute(.unique) let id = UUID().uuidString
    var date = Date()
    //... 其他属性
    
//    @Relationship(inverse: \Category.transactions)
    var category: Category?
    
    init(date: Date = Date(), balance: Double = 0.0, category: Category) {
        self.date = date
        self.balance = balance
        self.category = category // <-- 崩溃位置,已确认category地址有效
        category.add(transaction: self)
    }
}

@Model
class Category {
    @Attribute(.unique) let id = UUID().uuidString
    var name: String?
    //... 其他属性
    
    @Relationship(deleteRule: .cascade, inverse: \Transaction.category)
    var transactions: [Transaction]? = []

    init(name: String? = nil) {
        self.name = name
    }
    
    func add(transaction: Transaction) {
        if var transactions = self.transactions {
            transactions.append(transaction)
        }
    }
}

文件读取与数据创建代码

private func readFile(data: String) async {
    //... 解析文本行逻辑
    // 创建Transaction时触发问题
    let t = Transaction(date: date, category: category)
    transactions.append(t)
}

// 批量插入逻辑
await actor.insert(category) // 读取完成后插入Category

崩溃指向的SwiftData内部代码

{
    //    @Relationship(inverse: \Category.transactions)
          @storageRestrictions(accesses: _$backingData, initializes: _category)
    init(initialValue) {
        _$backingData.setValue(forKey: \.category, to: initialValue)
        _asset = _SwiftDataNoType()
    }
    //    @Relationship(inverse: \Category.transactions)
          get {
        _$observationRegistrar.access(self, keyPath: \.category)
        return self.getValue(forKey: \.category)
    }
    //    @Relationship(inverse: \Category.transactions)
          set {
        _$observationRegistrar.withMutation(of: self, keyPath: \.category) {
            self.setValue(forKey: \.category, to: newValue) <---- SwiftData内部崩溃位置
        }
    }
}

最终崩溃点代码

@main
struct DataImportWithProgressViewApp: App { <---- 崩溃点:Thread1: EXC_BAD_ACCESS (code=1, address=0x8000000000000010)
    let container = try! ModelContainer(
        for: Category.self
    )
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(container)
    }
}

排查方向与解决方案

  • 修复关系定义缺失:取消Transaction类中@Relationship(inverse: \Category.transactions)的注释,SwiftData需要明确的双向关系定义来正确管理对象关联,缺失反向关联会导致框架内部状态混乱。
  • 移除手动维护关联的代码:删除Transaction初始化方法中的category.add(transaction: self)调用,SwiftData会自动维护Category的transactions数组,手动修改会破坏框架的内部状态一致性,引发内存访问错误。
  • 确保模型对象在正确上下文创建:所有SwiftData模型对象的创建和修改必须在ModelContext所在的Actor上下文执行。如果使用自定义actor处理数据插入,需确保Transaction和Category的创建也在该Actor中完成,避免跨Actor访问未托管的模型对象。
  • 修正ModelContainer初始化:当前ModelContainer仅注册了Category,需同时包含Transaction,否则框架无法正确加载Transaction的模型元数据,导致关系处理失败。修改代码如下:
    let container = try! ModelContainer(
        for: Category.self, Transaction.self
    )
    
  • 优化批量插入逻辑:避免先创建大量未托管的模型对象再批量插入,建议在解析每条交易数据时,直接通过ModelContext的insert(_:)方法将对象插入上下文,确保模型对象始终处于框架的托管状态。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 22:46:06