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
相关产品推荐
相关产品推荐

