属性 具有通用(可选)用户默认值的包装器
Property Wrappers With Generic (Optional) User Defaults
With due reference:
我们已经开始为 UserDefaults 使用 属性 包装器,它可以与非可选属性无缝配合。
但是,设置可选 属性 的 nil 会崩溃:
[User Defaults] Attempt to set a non-property-list object as an
NSUserDefaults/CFPreferences value for key "someKeyThatWeSet"
Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: 'Attempt to insert non-property
list object null for key "someKeyThatWeSet"'
下面的代码可以直接在Playground上测试:
@propertyWrapper
struct C2AppProperty<T> {
let key: String
let defaultValue: T
init(_ key: String, defaultValue: T) {
self.key = key
self.defaultValue = defaultValue
}
var wrappedValue: T {
get {
return UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
}
set {
UserDefaults.standard.set(newValue, forKey: key)
}
}
}
struct C2User {
@C2AppProperty("userID", defaultValue: nil)
public static var publicUserID: String?
}
print(C2User.publicUserID)
C2User.publicUserID = "edusta"
print(C2User.publicUserID)
C2User.publicUserID = nil
print(C2User.publicUserID)
Expected:
nil
Optional<"edusta">
nil
Found:
nil
Optional<"edusta">
libc++abi.dylib: terminating with uncaught exception of type
NSException
到目前为止我尝试过的:
set {
// Comparing non-optional value of type 'T' to nil always returns false.
if newValue == nil {
UserDefaults.standard.removeObject(forKey: combinedKey)
} else {
UserDefaults.standard.set(newValue, forKey: combinedKey)
}
}
这里需要什么样的检查才能发现 newValue 是 nil
?或者 Optional<nil>
?
这段代码对我有用:
@propertyWrapper
struct UserDefault<T> {
private let key: String
private let defaultValue: T
private let userDefaults: UserDefaults
init(_ key: String, defaultValue: T, userDefaults: UserDefaults = .standard) {
self.key = key
self.defaultValue = defaultValue
self.userDefaults = userDefaults
}
var wrappedValue: T {
get {
guard let value = userDefaults.object(forKey: key) else {
return defaultValue
}
return value as? T ?? defaultValue
}
set {
if let value = newValue as? OptionalProtocol, value.isNil() {
userDefaults.removeObject(forKey: key)
} else {
userDefaults.set(newValue, forKey: key)
}
}
}
}
fileprivate protocol OptionalProtocol {
func isNil() -> Bool
}
extension Optional : OptionalProtocol {
func isNil() -> Bool {
return self == nil
}
}
With due reference:
我们已经开始为 UserDefaults 使用 属性 包装器,它可以与非可选属性无缝配合。
但是,设置可选 属性 的 nil 会崩溃:
[User Defaults] Attempt to set a non-property-list object as an NSUserDefaults/CFPreferences value for key "someKeyThatWeSet"
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to insert non-property list object null for key "someKeyThatWeSet"'
下面的代码可以直接在Playground上测试:
@propertyWrapper
struct C2AppProperty<T> {
let key: String
let defaultValue: T
init(_ key: String, defaultValue: T) {
self.key = key
self.defaultValue = defaultValue
}
var wrappedValue: T {
get {
return UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
}
set {
UserDefaults.standard.set(newValue, forKey: key)
}
}
}
struct C2User {
@C2AppProperty("userID", defaultValue: nil)
public static var publicUserID: String?
}
print(C2User.publicUserID)
C2User.publicUserID = "edusta"
print(C2User.publicUserID)
C2User.publicUserID = nil
print(C2User.publicUserID)
Expected:
nil
Optional<"edusta">
nil
Found:
nil
Optional<"edusta">
libc++abi.dylib: terminating with uncaught exception of type NSException
到目前为止我尝试过的:
set {
// Comparing non-optional value of type 'T' to nil always returns false.
if newValue == nil {
UserDefaults.standard.removeObject(forKey: combinedKey)
} else {
UserDefaults.standard.set(newValue, forKey: combinedKey)
}
}
这里需要什么样的检查才能发现 newValue 是 nil
?或者 Optional<nil>
?
这段代码对我有用:
@propertyWrapper
struct UserDefault<T> {
private let key: String
private let defaultValue: T
private let userDefaults: UserDefaults
init(_ key: String, defaultValue: T, userDefaults: UserDefaults = .standard) {
self.key = key
self.defaultValue = defaultValue
self.userDefaults = userDefaults
}
var wrappedValue: T {
get {
guard let value = userDefaults.object(forKey: key) else {
return defaultValue
}
return value as? T ?? defaultValue
}
set {
if let value = newValue as? OptionalProtocol, value.isNil() {
userDefaults.removeObject(forKey: key)
} else {
userDefaults.set(newValue, forKey: key)
}
}
}
}
fileprivate protocol OptionalProtocol {
func isNil() -> Bool
}
extension Optional : OptionalProtocol {
func isNil() -> Bool {
return self == nil
}
}