以更 Swift 的方式使用 UserDefaults。
前言 从文档简介来看,UserDefaults
为 Float
/ Double
/ Int
/ Bool
/ URL?
类型提供了具体的 set
方法,其他类型则调用 Any?
的 set
方法,实际上接受的是 NSData
/ NSString
/ NSNumber
/ NSDate
/ NSArray
/ NSDictionary
。如果非以上类型,Apple 建议转为 Data
再保存。
从 API 设计来看,set
方法相对友好,除了 Any?
/ URL?
使用了 Optional
,其他的都是具体类型。而 get
方法则较为混乱,bool(forKey:)
/ integer(forKey:)
/ float(forKey:)
/ double(forKey:)
的返回值是具体类型,即找不到就返回 false
/ 0
;array(forKey:)
/ dictionary(forKey:)
的返回值是 Any
,需要额外转换类型;stringArray(forKey:)
的返回值是 [String]
,其他类型却没有便利方法。
以下内容基于 Xcode 12.1,iPhone 12 Pro Max Simulator with iOS 14.1。
准备 1 2 3 4 5 6 7 8 struct Defaults <Value > { typealias Key = String typealias Value = Value let key: Key let value: Value }
首先定义 Defaults
结构体,保存 key
和 value
变量。
1 2 3 4 5 6 7 8 9 let bool = Defaults (key: "u_bool" , value: Bool (true ))let date = Defaults (key: "u_date" , value: Date (timeIntervalSince1970: 0 ))let double = Defaults (key: "u_double" , value: Double (1000 ))let float = Defaults (key: "u_float" , value: Float (100 ))let int = Defaults (key: "u_int" , value: Int (10 ))let string = Defaults (key: "u_string" , value: String ("@Nuomi1" ))let url = Defaults (key: "u_url" , value: URL (string: "https://nuomi1.github.io/" )! )let array = Defaults (key: "u_array" , value: [10 ])let dictionary = Defaults (key: "u_dictionary" , value: ["int" : 10 ])
根据 UserDefaults
的默认支持类型,选择了 Bool
/ Date
/ Double
/ Float
/ Int
/ String
/ URL
/ Array<Int>
/ Dictionary<String, Int>
进行测试,对应的桥接类型和自定义类型暂时忽略。
第一版 get-set-1 1 2 3 4 5 6 7 8 9 10 11 12 extension UserDefaults { func set1 <Value >(with defaults : Defaults <Value >) { set (defaults.value, forKey: defaults.key) } func value1 <Value >(with defaults : Defaults <Value >) -> Value ? { guard let object = value(forKey: defaults.key) else { return nil } return object as? Value } }
直接调用 UserDefaults
的 set
和 get
方法。
test-1a 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 func test1a <Value : Equatable >(with defaults : Defaults <Value >) { UserDefaults .standard.set1(with: defaults) let value = UserDefaults .standard.value1(with: defaults) assert (defaults.value == value) } test1a(with: bool) test1a(with: date) test1a(with: double) test1a(with: float) test1a(with: int) test1a(with: string) test1a(with: url) test1a(with: array) test1a(with: dictionary)
Key
Type
Value
u_bool
Number
1
u_date
Date
1970-01-01 08:00:00
u_double
Number
1000
u_float
Number
100
u_int
Number
10
u_string
String
@Nuomi1
u_url
ERROR
Thread 1: “Attempt to insert non-property list object https://nuomi1.github.io/ for key u_url”
u_array
Array<Number>
[10]
u_dictionary
Dictionary<String, Number>
[“int”: 10]
所有测试类型都是调用 Any?
的版本,而不是具体类型的版本。同时 URL
无法使用 Any?
的版本进行写入。
test-1b 1 2 3 4 5 6 7 8 9 10 11 12 13 14 func test1b (with defaults : Defaults <URL >) { UserDefaults .standard.set(defaults.value, forKey: defaults.key) let object = UserDefaults .standard.value(forKey: defaults.key) let url1 = object as? URL let url2 = (object as? Data ).flatMap { String (data: $0 , encoding: .utf8) } let url3 = (object as? Data ).flatMap { NSKeyedUnarchiver .unarchiveObject(with: $0 ) as? URL } assert (url1 == nil ) assert (url2 == nil ) assert (defaults.value == url3) } test1b(with: url)
Key
Type
Value
u_url
Data
<62706c69 73743030 d4010203 04050607 0a582476 65727369 6f6e5924 61726368 69766572 5424746f 7058246f 626a6563 74731200 0186a05f 100f4e53 4b657965 64417263 68697665 72d10809 54726f6f 748001a4 0b0c1314 55246e75 6c6cd30d 0e0f1011 12574e53 2e626173 65562463 6c617373 5b4e532e 72656c61 74697665 80008003 80025f10 19687474 70733a2f 2f6e756f 6d69312e 67697468 75622e69 6f2fd215 1617185a 24636c61 73736e61 6d655824 636c6173 73657355 4e535552 4ca21719 584e534f 626a6563 7408111a 24293237 494c5153 585e656d 74808284 86a2a7b2 bbc1c400 00000000 00010100 00000000 00001a00 00000000 00000000 00000000 0000cd>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 // u_url-1b.plist <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd" > <plist version ="1.0" > <dict > <key > $archiver</key > <string > NSKeyedArchiver</string > <key > $objects</key > <array > <string > $null</string > <dict > <key > $class</key > <dict > <key > CF$UID</key > <integer > 3</integer > </dict > <key > NS.base</key > <dict > <key > CF$UID</key > <integer > 0</integer > </dict > <key > NS.relative</key > <dict > <key > CF$UID</key > <integer > 2</integer > </dict > </dict > <string > https://nuomi1.github.io/</string > <dict > <key > $classes</key > <array > <string > NSURL</string > <string > NSObject</string > </array > <key > $classname</key > <string > NSURL</string > </dict > </array > <key > $top</key > <dict > <key > root</key > <dict > <key > CF$UID</key > <integer > 1</integer > </dict > </dict > <key > $version</key > <integer > 100000</integer > </dict > </plist >
不封装函数直接处理时,调用 URL?
的版本,但存储的类型是 Data
。把 object
对象进行类型转换,前面两个是失败的,只有第三个是成功的。
疑问 在 UserDefaults.swift#LL123 中,URL
可以被 Any?
的版本处理,且保存 absoluteURL.path
,实际上只能被 URL?
的版本处理。这里的原因可能是私有实现与开源实现并不一致。
小结 自带的 set
方法无法在一个方法里处理多种类型,下面我们借助 Codable
来实现。
第二版 get-set-2a 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 extension UserDefaults { func set2a <Value : Encodable >(with defaults : Defaults <Value >) { do { let encoder = JSONEncoder () let data = try encoder.encode(defaults.value) set (data, forKey: defaults.key) } catch { assertionFailure ("\(error) " ) return } } func value2a <Value : Decodable >(with defaults : Defaults <Value >) -> Value ? { guard let object = value(forKey: defaults.key) else { return nil } guard let data = object as? Data else { assertionFailure (); return nil } do { let decoder = JSONDecoder () let value = try decoder.decode(Value .self , from: data) return value } catch { assertionFailure ("\(error) " ) return nil } } }
借助 JSONEncoder
和 JSONDecoder
让 value
以 jsonData
的形式进行写入与读取。
test-2a 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 func test2a <Value : Codable & Equatable >(with defaults : Defaults <Value >) { UserDefaults .standard.set2a(with: defaults) let value = UserDefaults .standard.value2a(with: defaults) assert (defaults.value == value) } test2a(with: bool) test2a(with: date) test2a(with: double) test2a(with: float) test2a(with: int) test2a(with: string) test2a(with: url) test2a(with: array) test2a(with: dictionary)
Key
Type
Value
u_bool
Data
<74727565>
u_date
Data
<2d393738 33303732 3030>
u_double
Data
<31303030>
u_float
Data
<313030>
u_int
Data
<3130>
u_string
Data
<22404e75 6f6d6931 22>
u_url
Data
<22687474 70733a5c 2f5c2f6e 756f6d69 312e6769 74687562 2e696f5c 2f22>
u_array
Data
<5b31305d>
u_dictionary
Data
<7b22696e 74223a31 307d>
1 2 "https://nuomi1.github.io/"
1 2 // u_dictionary-2a.json {"int": 10}
jsonData
的内容实际上是 String
,可以看到 Double
/ Float
/ Int
的前半段是一样的值,都是 10
,Array<Int>
/ Dictionary<String, Int>
的中间也有一样的值,都是 10
。
get-set-2b 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 extension UserDefaults { func set2b <Value : Encodable >(with defaults : Defaults <Value >) { do { let encoder = PropertyListEncoder () let data = try encoder.encode(defaults.value) set (data, forKey: defaults.key) } catch { assertionFailure ("\(error) " ) return } } func value2b <Value : Decodable >(with defaults : Defaults <Value >) -> Value ? { guard let object = value(forKey: defaults.key) else { return nil } guard let data = object as? Data else { assertionFailure (); return nil } do { let decoder = PropertyListDecoder () let value = try decoder.decode(Value .self , from: data) return value } catch { assertionFailure ("\(error) " ) return nil } } }
改用 PropertyListEncoder
和 PropertyListDecoder
让 value
以 plistData
的形式进行写入与读取。
test-2b 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 func test2b <Value : Codable & Equatable >(with defaults : Defaults <Value >) { UserDefaults .standard.set2b(with: defaults) let value = UserDefaults .standard.value2b(with: defaults) assert (defaults.value == value) } test2b(with: bool) test2b(with: date) test2b(with: double) test2b(with: float) test2b(with: int) test2b(with: string) test2b(with: url) test2b(with: array) test2b(with: dictionary)
Key
Type
Value
u_bool
ERROR
Thread 1: Fatal error: invalidValue(true, Swift.EncodingError.Context(codingPath: [], debugDescription: “Top-level Bool encoded as number property list fragment.”, underlyingError: nil))
u_date
ERROR
Thread 1: Fatal error: invalidValue(1970-01-01 00:00:00 +0000, Swift.EncodingError.Context(codingPath: [], debugDescription: “Top-level Date encoded as date property list fragment.”, underlyingError: nil))
u_double
ERROR
Thread 1: Fatal error: invalidValue(1000.0, Swift.EncodingError.Context(codingPath: [], debugDescription: “Top-level Double encoded as number property list fragment.”, underlyingError: nil))
u_float
ERROR
Thread 1: Fatal error: invalidValue(100.0, Swift.EncodingError.Context(codingPath: [], debugDescription: “Top-level Float encoded as number property list fragment.”, underlyingError: nil))
u_int
ERROR
Thread 1: Fatal error: invalidValue(10, Swift.EncodingError.Context(codingPath: [], debugDescription: “Top-level Int encoded as number property list fragment.”, underlyingError: nil))
u_string
ERROR
Thread 1: Fatal error: invalidValue(“@Nuomi1”, Swift.EncodingError.Context(codingPath: [], debugDescription: “Top-level String encoded as string property list fragment.”, underlyingError: nil))
u_url
Data
<62706c69 73743030 d1010258 72656c61 74697665 5f101968 74747073 3a2f2f6e 756f6d69 312e6769 74687562 2e696f2f 080b1400 00000000 00010100 00000000 00000300 00000000 00000000 00000000 000030>
u_array
Data
<62706c69 73743030 a101100a 080a0000 00000000 01010000 00000000 00020000 00000000 00000000 00000000 000c>
u_dictionary
Data
<62706c69 73743030 d1010253 696e7410 0a080b0f 00000000 00000101 00000000 00000003 00000000 00000000 00000000 00000011>
1 2 3 4 5 6 7 8 9 // u_url-2b.plist <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd" > <plist version ="1.0" > <dict > <key > relative</key > <string > https://nuomi1.github.io/</string > </dict > </plist >
1 2 3 4 5 6 7 8 // u_array-2b.plist <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd" > <plist version ="1.0" > <array > <integer > 10</integer > </array > </plist >
1 2 3 4 5 6 7 8 9 // u_dictionary-2b.plist <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd" > <plist version ="1.0" > <dict > <key > int</key > <integer > 10</integer > </dict > </plist >
除了 URL
/ Array<Int>
/ Dictionary<String, Int>
可以被编码,其余的都失败了。
疑问 JSONEncoder
和 PropertyListEncoder
都是借助 Codable
进行编码,但是行为并不相同。
以 URL
为例,在 URL.swift#L1220 中,encode
时使用了 KeyedEncodingContainer<Key>
容器,这么看来 PropertyListEncoder
的行为是符合预期的,但是 JSONEncoder
直接编码 absoluteString
不符合预期。具体细节在 JSONEncoder.swift#L909 中。这里的猜想是,在 Swift 中 URL
是一个比较复杂的结构体,从其 encode
方法可以看到内部分为 base
和 relative
,拆分两部分可以更为完整地描述 URL
。但是在 JSON
中习惯使用 String
,而且没有单独的 URL
类型,所以 JSONEncoder
沿用了这一习惯直接编码 absoluteString
。
在 JSON
和 PropertyList
中,顶层的数据结构必须为数组或者字典,即 UnkeyedEncodingContainer
/ KeyedEncodingContainer<Key>
容器,PropertyListEncoder
的行为是符合预期的,但是 JSONEncoder
可以直接编码 Bool
/ Date
/ Double
/ Float
/ Int
/ String
,不符合 JSON
的规范。具体的原因是开启了 fragmentsAllowed
,允许非 Array
/ Dictionary
的类型作为顶层元素。
小结 在 JSON
中所有测试类型都可以成功编码,但在 PropertyList
中只有部分类型可以成功编码,下面我们借助 Wrapper
来实现。
第三版 1 2 3 4 5 6 7 8 9 10 11 extension UserDefaults { struct Wrapper <T > { let wrapped: T } } extension UserDefaults .Wrapper : Encodable where T : Encodable {}extension UserDefaults .Wrapper : Decodable where T : Decodable {}
先在 UserDefaults
里面定义一个 Wrapper<T>
结构体,把 UserDefaults
当做一个命名空间。然后声明当 T
遵循 Encodable
/ Decodable
时自身也遵循,更容易进行类型推导。
get-set-3a 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 extension UserDefaults { func set3a <Value : Encodable >(with defaults : Defaults <Value >) { do { let wrapper = Wrapper (wrapped: defaults.value) let encoder = JSONEncoder () let data = try encoder.encode(wrapper) set (data, forKey: defaults.key) } catch { assertionFailure ("\(error) " ) return } } func value3a <Value : Decodable >(with defaults : Defaults <Value >) -> Value ? { guard let object = value(forKey: defaults.key) else { return nil } guard let data = object as? Data else { assertionFailure (); return nil } do { let decoder = JSONDecoder () let wrapper = try decoder.decode(Wrapper <Value >.self , from: data) return wrapper.wrapped } catch { assertionFailure ("\(error) " ) return nil } } }
把 value
放进 Wrapper
,再使用 JSONEncoder
和 JSONDecoder
进行写入与读取。
test-3a 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 func test3a <Value : Codable & Equatable >(with defaults : Defaults <Value >) { UserDefaults .standard.set3a(with: defaults) let value = UserDefaults .standard.value3a(with: defaults) assert (defaults.value == value) } test3a(with: bool) test3a(with: date) test3a(with: double) test3a(with: float) test3a(with: int) test3a(with: string) test3a(with: url) test3a(with: array) test3a(with: dictionary)
Key
Type
Value
Count
u_bool
Data
<7b227772 61707065 64223a74 7275657d>
16
u_date
Data
<7b227772 61707065 64223a2d 39373833 30373230 307d>
22
u_double
Data
<7b227772 61707065 64223a31 3030307d>
16
u_float
Data
<7b227772 61707065 64223a31 30307d>
15
u_int
Data
<7b227772 61707065 64223a31 307d>
14
u_string
Data
<7b227772 61707065 64223a22 404e756f 6d693122 7d>
21
u_url
Data
<7b227772 61707065 64223a22 68747470 733a5c2f 5c2f6e75 6f6d6931 2e676974 6875622e 696f5c2f 227d>
42
u_array
Data
<7b227772 61707065 64223a5b 31305d7d>
16
u_dictionary
Data
<7b227772 61707065 64223a7b 22696e74 223a3130 7d7d>
22
所有测试类型全部通过,但是数据长度相比 test-2a 明显变大。
get-set-3b 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 extension UserDefaults { func set3b <Value : Encodable >(with defaults : Defaults <Value >) { do { let wrapper = Wrapper (wrapped: defaults.value) let encoder = PropertyListEncoder () let data = try encoder.encode(wrapper) set (data, forKey: defaults.key) } catch { assertionFailure ("\(error) " ) return } } func value3b <Value : Decodable >(with defaults : Defaults <Value >) -> Value ? { guard let object = value(forKey: defaults.key) else { return nil } guard let data = object as? Data else { assertionFailure (); return nil } do { let decoder = PropertyListDecoder () let wrapper = try decoder.decode(Wrapper <Value >.self , from: data) return wrapper.wrapped } catch { assertionFailure ("\(error) " ) return nil } } }
把 value
放进 Wrapper
,再使用 PropertyListEncoder
和 PropertyListDecoder
进行写入与读取。
test-3b 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 func test3b <Value : Codable & Equatable >(with defaults : Defaults <Value >) { UserDefaults .standard.set3b(with: defaults) let value = UserDefaults .standard.value3b(with: defaults) assert (defaults.value == value) } test3b(with: bool) test3b(with: date) test3b(with: double) test3b(with: float) test3b(with: int) test3b(with: string) test3b(with: url) test3b(with: array) test3b(with: dictionary)
Key
Type
Value
Count
u_bool
Data
<62706c69 73743030 d1010257 77726170 70656409 080b1300 00000000 00010100 00000000 00000300 00000000 00000000 00000000 000014>
55
u_date
Data
<62706c69 73743030 d1010257 77726170 70656433 c1cd27e4 40000000 080b1300 00000000 00010100 00000000 00000300 00000000 00000000 00000000 00001c>
63
u_double
Data
<62706c69 73743030 d1010257 77726170 70656423 408f4000 00000000 080b1300 00000000 00010100 00000000 00000300 00000000 00000000 00000000 00001c>
63
u_float
Data
<62706c69 73743030 d1010257 77726170 70656422 42c80000 080b1300 00000000 00010100 00000000 00000300 00000000 00000000 00000000 000018>
59
u_int
Data
<62706c69 73743030 d1010257 77726170 70656410 0a080b13 00000000 00000101 00000000 00000003 00000000 00000000 00000000 00000015>
56
u_string
Data
<62706c69 73743030 d1010257 77726170 70656457 404e756f 6d693108 0b130000 00000000 01010000 00000000 00030000 00000000 00000000 00000000 001b>
62
u_url
Data
<62706c69 73743030 d1010257 77726170 706564d1 03045872 656c6174 6976655f 10196874 7470733a 2f2f6e75 6f6d6931 2e676974 6875622e 696f2f08 0b13161f 00000000 00000101 00000000 00000005 00000000 00000000 00000000 0000003b>
96
u_array
Data
<62706c69 73743030 d1010257 77726170 706564a1 03100a08 0b131500 00000000 00010100 00000000 00000400 00000000 00000000 00000000 000017>
59
u_dictionary
Data
<62706c69 73743030 d1010257 77726170 706564d1 03045369 6e74100a 080b1316 1a000000 00000001 01000000 00000000 05000000 00000000 00000000 00000000 1c>
65
相比 test-2b,所有测试类型全部通过,但是数据长度相比 test-3a 变得更大。
小结 通过增加 wrapped
顶层,把数据全部转为字典,在 JSON
和 PropertyList
中都可以正常处理。同时可以看到,PropertyList
的数据长度比 JSON
大得多。准确性已经处理好了,但是 Data
没有可读性,接下来我们处理可读性。
第四版 get-set-4a 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 extension UserDefaults { func set4a <Value : Encodable >(with defaults : Defaults <Value >) { do { let wrapper = Wrapper (wrapped: defaults.value) let encoder = JSONEncoder () let data = try encoder.encode(wrapper) let object = try JSONSerialization .jsonObject( with: data ) set (object, forKey: defaults.key) } catch { assertionFailure ("\(error) " ) return } } func value4a <Value : Decodable >(with defaults : Defaults <Value >) -> Value ? { guard let object = value(forKey: defaults.key) else { return nil } do { let data = try JSONSerialization .data( withJSONObject: object ) let decoder = JSONDecoder () let wrapper = try decoder.decode(Wrapper <Value >.self , from: data) return wrapper.wrapped } catch { assertionFailure ("\(error) " ) return nil } } }
借助 JSONSerialization
让 data
与 object
互转。
test-4a 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 func test4a <Value : Codable & Equatable >(with defaults : Defaults <Value >) { UserDefaults .standard.set4a(with: defaults) let value = UserDefaults .standard.value4a(with: defaults) assert (defaults.value == value) } test4a(with: bool) test4a(with: date) test4a(with: double) test4a(with: float) test4a(with: int) test4a(with: string) test4a(with: url) test4a(with: array) test4a(with: dictionary)
所有测试类型全部通过。
get-set-4b 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 extension UserDefaults { func set4b <Value : Encodable >(with defaults : Defaults <Value >) { do { let wrapper = Wrapper (wrapped: defaults.value) let encoder = PropertyListEncoder () let data = try encoder.encode(wrapper) let object = try PropertyListSerialization .propertyList( from: data, format: nil ) set (object, forKey: defaults.key) } catch { assertionFailure ("\(error) " ) return } } func value4b <Value : Decodable >(with defaults : Defaults <Value >) -> Value ? { guard let object = value(forKey: defaults.key) else { return nil } do { let data = try PropertyListSerialization .data( fromPropertyList: object, format: .binary, options: 0 ) let decoder = PropertyListDecoder () let wrapper = try decoder.decode(Wrapper <Value >.self , from: data) return wrapper.wrapped } catch { assertionFailure ("\(error) " ) return nil } } }
借助 PropertyListSerialization
让 data
与 object
互转。
test-4b 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 func test4b <Value : Codable & Equatable >(with defaults : Defaults <Value >) { UserDefaults .standard.set4b(with: defaults) let value = UserDefaults .standard.value4b(with: defaults) assert (defaults.value == value) } test4b(with: bool) test4b(with: date) test4b(with: double) test4b(with: float) test4b(with: int) test4b(with: string) test4b(with: url) test4b(with: array) test4b(with: dictionary)
所有测试类型全部通过。
小结 借助 JSONSerialization
和 PropertyListSerialization
,把 data
转为 object
直接保存。
总结 借助 Codable
/ Wrapper
/ JSONSerialization
/ PropertyListSerialization
,我们成功地把 model
转为 object
进行保存,但是问题还是不少。
Wrapper
只是为了防止 PropertyListEncoder
出错而做的折中处理,多了一层嵌套实际上浪费了处理时间和保存容量。
JSONEncoder
和 PropertyListEncoder
内部已经调用了各自的 Serialization
把 model
-> object
-> data
,再把 data
转为 object
也是一种浪费。
JSONEncoder
和 PropertyListEncoder
不一致的处理会带来困惑,可以通过增加更多的 option
进行定制处理。
实际上可以参考 JSONEncoder
/ JSONDecoder
/ PropertyListEncoder
/ PropertyListDecoder
,实现 UserDefaultsEncoder
和 UserDefaultsDecoder
,直接把 model
转为 object
进行保存,同时根据自己的需要单独处理(例如 URL
)。
当然这又是另一个完整的解决方案了,以后可以尝试。
参考