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

Property Wrapper中@State变量是否可用?缓存UserDefaults问题咨询

关于Property Wrapper中使用@State的问题解答

首先明确结论:@State不能在自定义Property Wrapper里正常工作

@State是SwiftUI专属的属性包装器,它的核心作用是帮SwiftUI视图管理内部状态,其生命周期完全绑定在SwiftUI的视图系统中。你写的UserDefault是一个独立的自定义Property Wrapper,它的实例不会被SwiftUI视图托管,所以@State在这里根本发挥不了作用——这就是你的cacheValue始终为nil的原因。

无需额外编码的替代方案:用普通存储属性实现缓存

既然不能依赖@State,我们可以直接在Property Wrapper内部用一个普通的可选存储属性做缓存,同时在设置新值时同步更新缓存,所有逻辑都封装在Wrapper内部,使用它的类/结构体完全不需要额外编码。

修改后的代码如下:

@propertyWrapper struct UserDefault<T> {
    let key: String
    let defaultValue: T
    private var cachedValue: T? // 用普通存储属性替代@State做缓存
    
    init(_ key: String, defaultValue: T) {
        self.key = key
        self.defaultValue = defaultValue
    }
    
    var wrappedValue: T {
        get {
            if cachedValue == nil {
                cachedValue = UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
            }
            return cachedValue!
        }
        set {
            UserDefaults.standard.set(newValue, forKey: key)
            cachedValue = newValue // 同步更新缓存,避免下次读取再访问UserDefaults
        }
    }
}

进阶优化:处理UserDefaults的外部变更(可选)

如果你的App存在其他组件直接修改UserDefaults对应key值的场景,上面的缓存会和实际值不一致。我们可以给Property Wrapper添加监听逻辑,自动同步外部修改,同样不需要在使用端额外编码:

@propertyWrapper struct UserDefault<T> {
    let key: String
    let defaultValue: T
    private var cachedValue: T?
    private var observation: NSObjectProtocol?
    
    init(_ key: String, defaultValue: T) {
        self.key = key
        self.defaultValue = defaultValue
        
        // 监听UserDefaults全局变更通知
        observation = NotificationCenter.default.addObserver(
            forName: UserDefaults.didChangeNotification,
            object: UserDefaults.standard,
            queue: .main
        ) { [weak self] _ in
            guard let self = self else { return }
            // 收到通知后更新缓存为最新值
            self.cachedValue = UserDefaults.standard.object(forKey: self.key) as? T ?? self.defaultValue
        }
    }
    
    var wrappedValue: T {
        get {
            if cachedValue == nil {
                cachedValue = UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
            }
            return cachedValue!
        }
        set {
            UserDefaults.standard.set(newValue, forKey: key)
            cachedValue = newValue
        }
    }
    
    deinit {
        // 销毁时移除监听,避免内存泄漏
        if let observation = observation {
            NotificationCenter.default.removeObserver(observation)
        }
    }
}

使用示例

不管用上面哪一种实现,使用时都只需要简单声明即可,完全不需要额外代码:

struct AppSettings {
    @UserDefault("nickname", defaultValue: "Anonymous")
    static var nickname: String
    
    @UserDefault("autoLogin", defaultValue: false)
    static var autoLogin: Bool
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 06:42:53