UserDefaults可以存储一些基础数据类型,如:Data,String,Date,Bool,Int,Double,Float,Array,Dictionary,URL,等基础对象。也可以存储自定义对象(但需要实现编码成Data进行存储)。
尽管如此,我们还是不推荐使用UserDefaults来存储数据量大的数据。因为读写非常昂贵,userDefaults最终用.plist文件进行存储。存储大数据会使此文件变得臃肿。
对于自定义的对象,我们可以通过写文件方式将json写到沙盒文件中。或者存储到coreData. Sqlite等。UserDefaults一般用于存储一些简单的数据类型,存储用户的偏好设置等。在写少读多的情况下,性能才会高。底层是通过读写xml文件方式都数据进行读写。
接下来我们用PropertyWrapper对UserDefaults进行封装。1
2
3
4
5
6
7
8
9
struct UserDefault<T: PropertyListValue> {
let key: Key
var wrappedValue: T? {
get { UserDefaults.standard.value(forKey: key.rawValue) as? T }
set { UserDefaults.standard.set(newValue, forKey: key.rawValue) }
}
}
需要对传入的泛型数据做约束,遵循PropertyListValue协议1
2
3
4
5
6
7
8
9
10
11
12
13
14//定义约束协议
protocol PropertyListValue {}
extension Data: PropertyListValue {}
extension String: PropertyListValue {}
extension Date: PropertyListValue {}
extension Bool: PropertyListValue {}
extension Int: PropertyListValue {}
extension Double: PropertyListValue {}
extension Float: PropertyListValue {}
// 每个元素必须是 PropertyListValue 类型
extension Array: PropertyListValue where Element: PropertyListValue {}
extension Dictionary: PropertyListValue where Key == String, Value: PropertyListValue {}
然后我们定义Key的结构体,遵循 RawRepresentable 协议。1
2
3struct Key: RawRepresentable {
let rawValue: String
}
使用:1
2
3
4public struct StorageTest {
@UserDefault(key: Key(rawValue: "isFirstLaunch"))
var isFirstLaunchFlag: Bool?
}
为了达到@UserDefault(key: “isFirstLaunch”)这样的简写效果,我们让Key实现ExpressibleByStringLiteral协议。1
2
3
4
5
6
7
8
9
10extension Key: ExpressibleByStringLiteral {
init(stringLiteral: String) {
rawValue = stringLiteral
}
}
public struct StorageTest {
@UserDefault(key: "isFirstLaunch")
var isFirstLaunchFlag: Bool?
}
为了统一的管理,我们将存储键写在Key结构体中。1
2
3
4
5
6
7extension Key {
static let isFirstLaunch: Key = "isFirstLaunch"
}
public struct StorageTest {
@UserDefault(key: .isFirstLaunch)
var isFirstLaunchFlag: Bool?
}
KVO
1 | class DefaultsObservation: NSObject { |
用法:1
2
3
4
5
6
7
8var storage = Storage()
var observation = storage.$isFirstLaunch.observe { old, new in
print("Changed from: \(old) to \(new)")
}
storage.isFirstLaunch = true
storage.isFirstLaunch?.toggle()