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

SwiftUI中@Published属性包装器的性能开销多大?有没有更优方案

@Published 开销说明

@Published 本身的运行时开销极低,本质只是在属性的willSet阶段触发ObservableObject的objectWillChange发布者信号,单次触发开销为纳秒级,对于你这种单次加载静态头像的场景,完全不需要担心这部分的性能损耗。你现有实现的主要性能浪费来自两个问题:

  1. 用@ObservedObject托管ImageFetcher实例,每次父视图重绘触发ImageView初始化时,都会创建新的ImageFetcher实例,已经发起的请求会被废弃,还会重复发起网络请求
  2. 没有图片缓存机制,同一个头像每次进入页面都会重新请求
优化方案

按优先级从高到低排列:

1 修复现有实现的实例重复创建问题

把@ObservedObject换成@StateObject,让SwiftUI托管ImageFetcher的生命周期,保证实例只会创建一次,避免重复请求:

struct ImageView: View {
    @StateObject var fetcher: ImageFetcher

    init(_ firebaseManager: FirebaseManager) {
        self._fetcher = StateObject(wrappedValue: ImageFetcher(firebaseManager))
    }
    // body 代码保持不变
}

2 完全抛弃ObservableObject,使用更轻量的实现

对于单次异步加载的静态内容,不需要额外定义ObservableObject类,直接用@State托管加载状态和结果即可,没有发布者监听的额外开销:

// 全局图片缓存,避免重复请求
class GlobalImageCache: NSCache<NSString, UIImage> {
    static let shared = GlobalImageCache()
    private override init() {
        super.init()
        countLimit = 100 // 最多缓存100张头像
    }
}

struct ImageView: View {
    let firebaseManager: FirebaseManager
    @State private var image: UIImage?

    func fetchImage(id: String) async {
        // 先查缓存,命中直接返回
        if let cachedImage = GlobalImageCache.shared.object(forKey: id as NSString) {
            image = cachedImage
            return
        }
        let ref = firebaseManager.STORAGE.reference().child(id)
        do {
            let data = try await ref.getData(maxSize: 2051240)
            guard let uiImage = UIImage(data: data) else { return }
            GlobalImageCache.shared.setObject(uiImage, forKey: id as NSString)
            image = uiImage
        } catch {
            print("加载失败: \(error.localizedDescription)")
        }
    }

    var body: some View {
        Button {
            Task {
                await fetchImage(id: "QDhKhNe5VnWDCORoQScIVVzcpzv2")
            }
        } label: {
            Text("Load Image")
        }

        if let image = image {
            Image(uiImage: image)
                .resizable()
                .frame(width: 50, height: 50)
        } else {
            Text("Loading...")
        }
    }
}

3 优先使用系统原生AsyncImage(iOS 15+)

如果你的项目最低支持iOS15,直接使用系统提供的AsyncImage即可,内部自动处理加载状态、缓存、生命周期管理,代码最精简,开销最低:

struct ImageView: View {
    let firebaseManager: FirebaseManager
    @State private var imageURL: URL?

    func getDownloadURL(uid: String) async {
        let ref = firebaseManager.STORAGE.reference().child(uid)
        imageURL = try? await ref.downloadURL()
    }

    var body: some View {
        Button {
            Task {
                await getDownloadURL(uid: "QDhKhNe5VnWDCORoQScIVVzcpzv2")
            }
        } label: {
            Text("Load Image")
        }

        if let url = imageURL {
            AsyncImage(url: url) { phase in
                switch phase {
                case .empty: Text("Loading...")
                case .success(let image):
                    image
                        .resizable()
                        .frame(width: 50, height: 50)
                case .failure: Text("加载失败")
                @unknown default: EmptyView()
                }
            }
        } else {
            Text("点击加载图片")
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 12:15:06