所以我有了為 UserDefaults 創建一個過期時間的想法。這是我開始采用的方法,但我被卡住了。
struct TimedObject<T: Codable>: Codable {
let object: T
let expireDate: Date
}
接著:
extension UserDefaults {
func set<T: Codable>(_ value: T, forKey key: String, expireDate: Date) {
let timedObject = TimedObject<T>(object: value, expireDate: expireDate)
let encoder = JSONEncoder()
if let encoded = try? encoder.encode(timedObject) {
UserDefaults.standard.set(encoded, forKey: key)
}
override open class func value(forKey key: String) -> Any? {
guard let value = self.value(forKey: key) else {
return nil
}
if TimedObject<???>.self == type(of: value) { // This is where I'm stuck
...
}
}
因此,如果我命名型別而不使用泛型,我會很容易地解決這個問題。但自然我更喜歡使用泛型。這可以做到嗎?
uj5u.com熱心網友回復:
我知道 OP 正在使用結構來包裝存盤的值,但我仍然想提供一個不同的基于協議的解決方案,其中任何應該以到期日期存盤的型別都需要符合這個協議。
這是我正在使用的協議
protocol TimedObject: Codable {
associatedtype Value: Codable
var value: Value { get }
var expirationDate: Date { get }
}
以及存盤和檢索的功能UserDefaults
extension UserDefaults {
func set<Timed: TimedObject>(_ value: Timed, forKey key: String) {
if let encoded = try? JSONEncoder().encode(value) {
self.set(encoded, forKey: key)
}
}
func value<Timed: TimedObject>(_ type: Timed.Type, forKey key: String) -> Timed.Value? {
guard let data = self.value(forKey: key) as? Data, let object = try? JSONDecoder().decode(Timed.self, from: data) else {
return nil
}
return object.expirationDate > .now ? object.value : nil
}
}
最后一個例子
struct MyStruct: Codable {
let id: Int
let name: String
}
extension MyStruct: TimedObject {
typealias Value = Self
var value: MyStruct { self }
var expirationDate: Date {
.now.addingTimeInterval(24 * 60 * 60)
}
}
let my = MyStruct(id: 12, name: "abc")
UserDefaults.standard.set(my, forKey: "my")
let my2 = UserDefaults.standard.value(MyStruct.self, forKey: "my")
uj5u.com熱心網友回復:
由于您要回傳Any?,因此最好創建另一個結構來指向,TimedObject因為回傳時不需要物件屬性Any?:
struct Expiry: Codable {
var expireDate: Date
}
struct TimedObject<T: Codable>: Timable {
let object: T
var expireDate: Date
}
override open class func value(forKey key: String) -> Any? {
guard let value = self.value(forKey: key) as? Data else {
return nil
}
if let timed = try? JSONDecoder().decode(Expiry.self, from: value) {
//do anything with timed
}
//make sure to return value
}
這是一種訪問方法TimedObject:
func timedObject<T: Codable>(forKey key: String) -> TimedObject<T>? {
guard let value = self.data(forKey: key) as? Data, let timed = try? JSONDecoder().decode(TimedObject<T>.self, from: value) else {
return nil
}
return value
}
uj5u.com熱心網友回復:
如果您使value()方法通用,那么您可以反轉方法中完成的程序set():檢索資料并將其解碼為TimedObject<T>.
但是,我會選擇一個不同的名稱以避免與現有value(forKey:)方法可能產生的歧義。我也看不出為什么這應該是一個類方法。
另請注意,您的泛型set()方法應在同一實體上呼叫非泛型版本。
extension UserDefaults {
func set<T: Codable>(_ value: T, forKey key: String, expireDate: Date) {
let timedObject = TimedObject(object: value, expireDate: expireDate)
let encoder = JSONEncoder()
if let encoded = try? encoder.encode(timedObject) {
set(encoded, forKey: key)
}
}
func expiringValue<T: Codable>(forKey key: String) -> T? {
guard let data = self.data(forKey: key) else {
return nil
}
let decoder = JSONDecoder()
guard let decoded = try? decoder.decode(TimedObject<T>.self, from: data) else {
return nil
}
// check expire date ...
return decoded.object
}
}
示例用法:
let val1 = UserDefaults.standard.expiringValue(forKey: "foo") as String?
let val2: String? = UserDefaults.standard.expiringValue(forKey: "bar")
在這兩種情況下,expiringValue(forKey:)都使用推斷型別呼叫。
或結合可選系結:
if let val: String = UserDefaults.standard.expiringValue(forKey: "test") {
print(val)
}
另一種選擇是將所需型別作為附加引數傳遞:
func value<T: Codable>(forKey key: String, as: T.Type) -> T? {
guard let data = self.data(forKey: key) else {
return nil
}
let decoder = JSONDecoder()
guard let decoded = try? decoder.decode(TimedObject<T>.self, from: data) else {
return nil
}
// check expire date ...
return decoded.object
}
然后用作
let val = UserDefaults.standard.value(forKey: "foo", as: String.self)
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/506764.html
上一篇:在Java中容器化可執行檔案
下一篇:無法分配給通用結構欄位
