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

SwiftUI中计算属性与带参函数的性能差异及缓存问题

SwiftUI计算属性与函数的缓存问题解答

一、普通计算属性确实不会缓存

你的判断是对的:这类普通计算属性没有任何缓存机制。

在SwiftUI里,只要视图的@State变量发生变化,整个视图的渲染流程就会重新执行——所有当前渲染用到的计算属性,不管它们依赖的数据源有没有改动,都会被重新求值。比如你修改c的值时,computation1会被调用,而它内部引用了aTimesB,哪怕a和b压根没变化,aTimesB也会被重新计算一次,纯属于不必要的性能浪费。要是实际项目里这类计算数量多、开销大,很容易拖慢主线程。

二、普通带参数函数也不会自动缓存

改成带参数的函数后,默认还是不会缓存结果。

Swift的普通函数没有内置缓存逻辑,不管你传入的参数和上次是不是完全一致,每次调用都会从头执行一遍代码。你给出的函数示例里,哪怕a和b没变化,每次调用aTimesB(a:,b:)都会重新计算a*b,根本不会复用之前的结果,所以性能上不会有任何提升。

三、真正有效的缓存优化方法

要实现“参数不变就复用结果”的效果,得手动添加缓存逻辑,常用的有这几种:

1. 使用@Memoized(Swift 5.9及以上版本)

Swift 5.9新增的@Memoized属性包装器,能直接给函数添加缓存能力,自动根据输入参数存储计算结果:

@State var a: Double
@State var b: Double
@State var c: Double

@Memoized
func aTimesB(a: Double, b: Double) -> Double {
    return a * b
}

@Memoized
func bTimesC(b: Double, c: Double) -> Double {
    return b * c
}

@Memoized
func computation1(item1: Double, item2: Double) -> Double {
    return doSuperExpensiveThingWith(item1 + item2)
}

只要传入的参数和上次一致,函数就会直接返回缓存的结果,无需重复计算,是最简单高效的方案。

2. 手动实现缓存逻辑(兼容旧版本Swift)

如果项目还没升级到Swift 5.9,可以自己用字典存储参数与结果的映射,建议把缓存放到@StateObject中管理,避免视图重绘时缓存丢失:

@State var a: Double
@State var b: Double
@State var c: Double

@StateObject private var calculator = Calculator()

private class Calculator: ObservableObject {
    // 用字典存储每个函数的缓存,键用参数拼接成字符串
    private var aTimesBCache: [String: Double] = [:]
    private var bTimesCCache: [String: Double] = [:]
    private var computation1Cache: [String: Double] = [:]
    
    func aTimesB(a: Double, b: Double) -> Double {
        let key = "\(a)-\(b)"
        // 先查缓存,存在则直接返回
        if let cached = aTimesBCache[key] {
            return cached
        }
        // 无缓存则计算并存储
        let result = a * b
        aTimesBCache[key] = result
        return result
    }
    
    // 同理实现bTimesC和computation1的缓存逻辑
    func bTimesC(b: Double, c: Double) -> Double {
        let key = "\(b)-\(c)"
        if let cached = bTimesCCache[key] {
            return cached
        }
        let result = b * c
        bTimesCCache[key] = result
        return result
    }
    
    func computation1(item1: Double, item2: Double) -> Double {
        let key = "\(item1)-\(item2)"
        if let cached = computation1Cache[key] {
            return cached
        }
        let result = doSuperExpensiveThingWith(item1 + item2)
        computation1Cache[key] = result
        return result
    }
}

// 使用时调用calculator的方法即可
// let finalResult = calculator.computation1(item1: calculator.aTimesB(a: a, b: b), item2: calculator.bTimesC(b: b, c: c))

3. 将计算逻辑移至ViewModel中缓存

如果计算依赖的是@State变量,也可以把计算属性放到ObservableObject类型的ViewModel里,自己追踪依赖变化来实现缓存:

class ViewModel: ObservableObject {
    @Published var a: Double = 0
    @Published var b: Double = 0
    @Published var c: Double = 0
    
    // 缓存a*b的结果,以及上次的a、b值
    private var cachedATimesB: Double?
    private var lastA: Double?
    private var lastB: Double?
    
    var aTimesB: Double {
        // 只有a或b发生变化时,才重新计算
        if let cached = cachedATimesB, lastA == a, lastB == b {
            return cached
        }
        let result = a * b
        cachedATimesB = result
        lastA = a
        lastB = b
        return result
    }
    
    // 用同样逻辑实现bTimesC和computation1
}

这样只有当依赖的变量真的变化时,才会重新计算,否则直接返回缓存值。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 21:33:23