05、hyperoslo cache学习

描述

在这个领域里,Cache并不是独一无二的,但它并不是另一个让你拥有上帝力量的怪物库。
除了缓存,它什么都不做,但它做得很好。
它提供了一个良好的公共 API,它有开箱即用的实现和巨大的自定义可能性。
高速缓存在 Swift 4 中使用Codable来执行序列化。

关键特性

  • 与 Swift 4 Codable合作。任何符合Codable的东西都将被Storage轻松地保存和加载。
  • 默认磁盘存储。可选地使用内存存储来启用混合.
  • 选项通过DiskConfig和MemoryConfig.
  • 支持过期和清除过期的对象.
  • 线程安全的。可以从任何队列访问操作.
  • 默认情况下同步。还支持异步 APIs.
  • 存储图像通过ImageWrapper.
  • 广泛的单元测试覆盖面和文档.
  • 支持 iOS, tvOS 和 macOS.

使用

Storage

Cache是建立基于责任链模式,在其中有许多处理对象,每个处理对象都知道如何执行 1 个任务,并委托给下一个任务。
但这只是实现细节。您需要知道的是Storage,它保存并加载Codable对象。
Storage有磁盘存储和可选的内存存储。
内存存储应该更少时间和内存消耗,而磁盘存储用于满足应用程序生命周期的内容,更像一种存储用户信息的方便方式,这些信息应该在应用程序启动时持久化。

DiskConfig是必需的,设置磁盘存储。您可以选择通过MemoryConfig来将内存作为前端存储。

let diskConfig = DiskConfig(name: "Floppy")
let memoryConfig = MemoryConfig(expiry: .never, countLimit: 10, totalCostLimit: 10)

let storage = try? Storage(diskConfig: diskConfig, memoryConfig: memoryConfig)

Codable 类型

Storage支持任何符合Codable协议对象。
你可以做你自己的事情符合 Codable,这样可以保存和加载Storage
支持的类型:

  • 原语如 Int, Float, String, Bool, …
  • 数组的元素如 [Int], [Float], [Double], …
  • 基本类型的 Set 如 Set, Set, …
  • 简单的字典 [String: Int], [String: String], …
  • Date
  • URL
  • Data

错误处理

错误处理是通过try catch完成的。Storage在StorageError方面抛出错误。

public enum : Error {
  
  case notFound
  /// 对象被找到,但是被请求的类型失败了
  case typeNotMatch
  /// 文件属性是畸形的
  case malformedFileAttributes
  /// 不能执行解码
  case decodingFailed
  /// 不能执行编码
  case encodingFailed
  /// 存储已经被释放了
  case deallocated
}

在从存储中加载时,可能会出现磁盘问题或类型不匹配的错误,因此,如果要处理错误,则需要try catch

do {
  let storage = try Storage(diskConfig: diskConfig, memoryConfig: memoryConfig)
} catch {
  print(error)
}

配置

下面是如何使用多种配置选项

let diskConfig = DiskConfig(
  //磁盘存储的名称,这将用作目录中的文件夹名称
  name: "Floppy",
  //在默认情况下,每个添加的对象都将使用该过期日期
  //如果它没有在`setObject(forKey:expiry:)`方法中被覆盖
  expiry: .date(Date().addingTimeInterval(2*3600)),
  maxSize: 10000,
  // 存储磁盘缓存的位置。如果nil,它被放置在cachesDirectory目录中.
  directory: try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask,
                                          appropriateFor: nil, create: true).appendingPathComponent("MyPreferences"),
  // 数据保护用于在磁盘上以加密格式存储文件,并根据需要对其进行解密
  protectionType: .complete
)
let memoryConfig = MemoryConfig(
  //在默认情况下,每个添加的对象都将使用该过期日期
  //如果它没有在`setObject(forKey:expiry:)`方法中被覆盖
  expiry: .date(Date().addingTimeInterval(2*60)),
  /// 内存中的对象的最大数量应该保持在内存中
  countLimit: 50,
  /// 缓存在开始清除对象之前所能容纳的最大总成本
  totalCostLimit: 0
)

在 iOS 平台上,tvOS 还可以在DiskConfig上指定protectionType,在应用程序的容器中为存储在磁盘上的文件添加安全级别。
有关更多信息,请参见FileProtectionType

同步 APIs

缺省情况下,Storage是同步的,是线程安全的,您可以从任何队列访问它。
所有同步功能都受到StorageAware协议的约束。

// 保存到storage
try? storage.setObject(10, forKey: "score")
try? storage.setObject("Oslo", forKey: "my favorite city", expiry: .never)
try? storage.setObject(["alert", "sounds", "badge"], forKey: "notifications")
try? storage.setObject(data, forKey: "a bunch of bytes")
try? storage.setObject(authorizeURL, forKey: "authorization URL")

// 从storage加载
let score = try? storage.object(ofType: Int.self, forKey: "score")
let favoriteCharacter = try? storage.object(ofType: String.self, forKey: "my favorite city")

// 检查对象是否存在
let hasFavoriteCharacter = try? storage.existsObject(ofType: String.self, forKey: "my favorite city")

// 删除存储中的对象
try? storage.removeObject(forKey: "my favorite city")

// 删除所有对象
try? storage.removeAll()

// 删除过期的对象
try? storage.removeExpiredObjects()

Entry

您可以使用它的过期信息和元数据来获取对象。您可以使用 Entry

let entry = try? storage.entry(ofType: String.self, forKey: "my favorite city")
print(entry?.object)
print(entry?.expiry)
print(entry?.meta)

如果从磁盘存储器中取出对象,meta可能包含文件信息。

自定义 Codable

Codable适用于像[String: Int], [String: String], … 这样的简单字典
它并不适用于[String: Any],因为Any不符合Codable的一致性,它将在运行时引发致命错误。因此,当您从后端响应中获得 json 时,您需要将其转换为您的自定义Codable对象,并将其保存为存储。

struct User: Codable {
  let firstName: String
  let lastName: String
}

let user = User(fistName: "John", lastName: "Snow")
try? storage.setObject(user, forKey: "character")

异步 APIs

async方式中,您处理Result而不是try catch,因为 result 是在稍后的时间交付的,以避免阻塞当前的调用队列。在完成块中,您要么有value,要么有error

您可以通过storage.async访问异步 APIs,它也是线程安全的,您可以按照您想要的任何顺序使用同步和异步 APIs。所有的异步函数都受到AsyncStorageAware协议的约束。

storage.async.setObject("Oslo", forKey: "my favorite city") { result in
  switch result {
    case .value:
      print("saved successfully")
    case .error(let error):
      print(error)
    }
  }
}

storage.async.object(ofType: String.self, forKey: "my favorite city") { result in
  switch result {
    case .value(let city):
      print("my favorite city is (city)")
    case .error(let error):
      print(error)
    }
  }
}

storage.async.existsObject(ofType: String.self, forKey: "my favorite city") { result in
  if case .value(let exists) = result, exists {
    print("I have a favorite city")
  }
}

storage.async.removeAll() { result in
  print("removal completes")
}

storage.async.removeExpiredObjects() { result in
  print("removal completes")
}

到期日期

默认情况下,所有保存的对象与您在DiskConfig或MemoryConfig中指定的到期时间具有相同的过期时间。可以通过指定setObject的expiry来覆盖特定对象。

// 配置的默认过期日期将被应用到项目中
try? storage.setObject("This is a string", forKey: "string")

// 给定的过期日期
try? storage.setObject(
  "This is a string",
  forKey: "string"
  expiry: .date(Date().addingTimeInterval(2 * 3600))
)

// 清除过期的对象
storage.removeExpiredObjects()

关于图片?

如您所知,NSImage和UIImage在默认情况下不符合Codable。
为了使它在可编程序中运行良好,我们引入了ImageWrapper,这样您就可以保存和加载图像

let wrapper = ImageWrapper(image: starIconImage)
try? storage.setObject(wrapper, forKey: "star")

let icon = try? storage.object(ofType: ImageWrapper.self, forKey: "star").image

如果你想把图像载入 UIImageView 或 NSImageView,那么我们也有一个很好的礼物给你。叫做Imaginary,并使用Cache下罩时,让你的生活更容易处理远程图像。

处理 JSON 响应

大多数情况下,我们的用例是从后端获取 json,并将 json 存储到存储中,以便将来使用。如果你使用库Alamofire或Malibu,你是得到 json 形式的字典,字符串,或数据。

Storage可以持久存储字符串或数据。您甚至可以使用JSONArrayWrapper和JSONDictionaryWrapper将 json 保存到Storage中,但是我们更喜欢持久化强类型的对象,因为这些对象是您将在 UI 中显示的对象。此外,如果 json 数据不能转换为强类型的对象,那么保存它的意义是什么呢?😉

您可以在JSONDecoder上使用这些扩展来解码 json 字典、字符串或数据到对象。

let user = JSONDecoder.decode(jsonString, to: User.self)
let cities = JSONDecoder.decode(jsonDictionary, to: [City].self)
let dragons = JSONDecoder.decode(jsonData, to: [Dragon].self)

这就是如何使用Alamofire执行对象转换和保存。

Alamofire.request("https://gameofthrones.org/mostFavoriteCharacter").responseString { response in
  do {
    let user = try JSONDecoder.decode(response.result.value, to: User.self)
    try storage.setObject(user, forKey: "most favorite character")
  } catch {
    print(error)
  }
}

原链接:移动端 hyperoslo cache介绍

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值