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

SwiftData对象传参未用inout却修改原数组的问题求助

问题根源

你遇到的问题核心是SwiftData的@Model类属于引用类型,数组赋值(比如var tmp = accounts)只是复制了数组中对象的引用,而非对象本身的深拷贝。所以你修改数组里的元素时,实际操作的还是原SwiftData存储中的实例,进而触发@Query的更新,导致groupedAccounts反复计算,最终内存耗尽。


解决方案1:实现Model对象的深拷贝

给Account模型添加深拷贝方法,手动复制所有属性(包括嵌套集合),确保操作的是独立副本:

extension Account {
    func deepCopy() -> Account {
        let copy = Account()
        // 复制所有基础属性
        copy.id = self.id
        copy.name = self.name
        copy.parentAccountID = self.parentAccountID
        copy.currency = self.currency
        copy.budget = self.budget
        copy.remainder = self.remainder
        copy.visible = self.visible
        copy.accounting = self.accounting
        copy.isChild = self.isChild
        // 深拷贝子账户(根据业务逻辑调整)
        copy.childrenAccounts = self.childrenAccounts.map { $0.deepCopy() }
        return copy
    }
}

然后在groupAccounts函数开头,先创建整个数组的深拷贝,后续操作都基于这个副本:

func groupAccounts(accounts: [Account], currencies: [Currency]) -> [Account] {
    // 生成独立副本数组
    var accountsCopy = accounts.map { $0.deepCopy() }
    
    // 示例代码修改为操作副本
    print(accountsCopy[0].name)
    accountsCopy[0].name += "Some" // 仅修改副本,不影响原数据
    
    var ratesMap: [String: Decimal] {
        Dictionary(uniqueKeysWithValues: currencies.map { ($0.isoCode, $0.rate ) })
    }
        
    for (i, account) in accountsCopy.enumerated() {
        if let parentAccountID = account.parentAccountID {
            guard let parentIndex = accountsCopy.firstIndex({ $0.id == parentAccountID }) else {
                continue // 避免强制解包崩溃
            }
            
            if account.visible {
                accountsCopy[parentIndex].childrenAccounts.append(account)
                if account.accounting {
                    let parentRate = ratesMap[accountsCopy[parentIndex].currency] ?? 1
                    let accountRate = ratesMap[account.currency] ?? 1
                    let relation = parentRate / accountRate
                    accountsCopy[parentIndex].budget += account.budget * relation
                    accountsCopy[parentIndex].remainder += account.remainder * relation
                }
            }
            accountsCopy[i].isChild = true
        }
    }
    return accountsCopy
}

解决方案2:避免重复计算groupedAccounts

当前groupedAccounts是计算属性,每次访问都会重新调用groupAccounts,加上原数据变更触发@Query更新,会导致无限循环。可以用以下方式缓存结果:

方式1:使用@Memoized(iOS17+/macOS14+)

struct AccountCircleList: View {
    @Query var accounts: [Account]
    @Query var currencies: [Currency]
    
    // 仅当accounts/currencies变更时重新计算
    @Memoized
    var groupedAccounts: [Account] {
        groupAccounts(accounts: accounts, currencies: currencies)
    }
    ...
}

方式2:手动缓存(兼容旧系统)

struct AccountCircleList: View {
    @Query var accounts: [Account]
    @Query var currencies: [Currency]
    @State private var cachedGroupedAccounts: [Account] = []
    
    var body: some View {
        // 你的视图内容
    }
    .onChange(of: [accounts, currencies]) { _, _ in
        cachedGroupedAccounts = groupAccounts(accounts: accounts, currencies: currencies)
    }
    .onAppear {
        cachedGroupedAccounts = groupAccounts(accounts: accounts, currencies: currencies)
    }
}

附加问题:无需直接传入汇率货币的方案

利用SwiftData的ModelContext直接查询所需汇率,不用传递整个currencies数组:

// 注入ModelContext到函数
func groupAccounts(accounts: [Account], context: ModelContext) -> [Account] {
    var accountsCopy = accounts.map { $0.deepCopy() }
    
    // 仅查询用到的币种,提升效率
    let usedCurrencies = Set(accounts.flatMap { [$0.currency] })
    let fetchDescriptor = FetchDescriptor<Currency>(
        predicate: #Predicate { usedCurrencies.contains($0.isoCode) }
    )
    guard let currencies = try? context.fetch(fetchDescriptor) else {
        return accountsCopy
    }
    
    let ratesMap = Dictionary(uniqueKeysWithValues: currencies.map { ($0.isoCode, $0.rate) })
    // 后续逻辑不变...
    return accountsCopy
}

调用时传入视图的modelContext:

struct AccountCircleList: View {
    @Query var accounts: [Account]
    @Environment(\.modelContext) private var context
    
    @Memoized
    var groupedAccounts: [Account] {
        groupAccounts(accounts: accounts, context: context)
    }
    ...
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 21:10:00