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

SwiftUI使用ObservableObject时如何避免不必要重渲染?性能问题与解决

问题解答

1. 会不会引发性能问题?

会。哪怕当前视图只有简单的Text,频繁(比如每0.1秒一次)的无意义重渲染也会持续占用CPU资源,长期下来会导致设备耗电增加;如果视图结构复杂(比如包含大量子视图、动画、或者需要计算的内容),还会直接引发界面卡顿,影响用户体验。

2. 兼容iOS17以下的解决办法

方法一:赋值前手动判断值是否变化

最简单的方式就是在给test赋值前,先判断当前值和新值是否不同,只有不同时才执行赋值:

func loadTimer(){
    timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { _ in
        guard self.test != false else { return }
        self.test = false
    }
}

这种方式适合单个属性的简单场景,直接避免无意义的@Published通知触发。

方法二:自定义仅在值变化时触发通知的属性包装器

如果项目里有多个类似场景,可以封装一个自定义的属性包装器,利用Equatable协议自动判断值是否变化,只有真正变化时才发送更新通知:

import SwiftUI
import Combine

@propertyWrapper
struct EquatablePublished<Value: Equatable>: Publisher {
    typealias Output = Value
    typealias Failure = Never
    
    @Published private var value: Value
    
    var wrappedValue: Value {
        get { value }
        set {
            guard value != newValue else { return }
            value = newValue
        }
    }
    
    var projectedValue: Published<Value>.Publisher { $value }
    
    init(wrappedValue initialValue: Value) {
        self._value = Published(wrappedValue: initialValue)
    }
    
    func receive<S>(subscriber: S) where S : Subscriber, Never == S.Failure, Value == S.Input {
        $value.receive(subscriber: subscriber)
    }
}

然后把ViewModel里的@Published替换成这个自定义包装器:

class ViewModel:ObservableObject{
    @EquatablePublished var test = false
    var timer:Timer!
    init(){
    }
    func loadTimer(){
        timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { _ in
            self.test = false
        }
    }
}

这样不管你怎么重复赋值相同值,都不会触发视图更新通知。

方法三:拆分视图减少重渲染范围

如果无法从ViewModel层面解决,可以把视图中不依赖test属性的部分拆成独立子视图。SwiftUI会自动优化子视图的渲染,只有当子视图依赖的状态变化时才会重渲染。比如:

struct StaticContentView: View {
    var body: some View {
        Text("Hello, world!")
    }
}

struct ContentView: View {
    @StateObject var viewModel = ViewModel()
    var body: some View {
        let _ = Self._printChanges()
        VStack {
            StaticContentView()
        }
        .onAppear{
            viewModel.loadTimer()
        }
        .padding()
    }
}

不过这个方法是“治标不治本”,最好还是从源头阻止无意义的状态更新通知。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 17:41:16