-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathSTCache.swift
More file actions
130 lines (113 loc) · 3.38 KB
/
Copy pathSTCache.swift
File metadata and controls
130 lines (113 loc) · 3.38 KB
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//
// STCache.swift
// STBaseProject
//
// Created by 寒江孤影 on 2019/3/16.
//
import Foundation
#if canImport(UIKit)
import UIKit
#endif
/// 轻量级内存缓存
///
/// - 线程安全:基于并发队列+栅栏保护内部字典
/// - 自动在收到内存警告时清空(仅 iOS)
/// - 可选容量上限,达到上限时淘汰最近最少使用的元素
public final class STCache<Key: Hashable, Value> {
private var store: [Key: Value] = [:]
private var keysByRecentUse: [Key] = []
private let queue = DispatchQueue(label: "com.stbaseproject.cache", attributes: .concurrent)
private var limit: Int = 0
#if canImport(UIKit)
private var memoryWarningObserver: NSObjectProtocol?
#endif
/// 初始化
/// - Parameter itemLimit: 元素数量上限,0 表示无限
public init(itemLimit: Int = 0) {
self.limit = itemLimit
registerMemoryWarningObserver()
}
deinit {
#if canImport(UIKit)
if let observer = memoryWarningObserver {
NotificationCenter.default.removeObserver(observer)
}
#endif
}
/// 元素数量
public var count: Int {
queue.sync { store.count }
}
/// 元素上限(0 表示无限)
public var itemLimit: Int {
get { queue.sync { limit } }
set {
queue.async(flags: .barrier) {
self.limit = max(0, newValue)
self.trimToLimitIfNeeded()
}
}
}
public subscript(key: Key) -> Value? {
get { object(forKey: key) }
set {
if let newValue {
setObject(newValue, forKey: key)
} else {
removeObject(forKey: key)
}
}
}
public func object(forKey key: Key) -> Value? {
queue.sync(flags: .barrier) {
guard let value = store[key] else { return nil }
markAsRecentlyUsed(key)
return value
}
}
public func setObject(_ object: Value, forKey key: Key) {
queue.async(flags: .barrier) {
self.store[key] = object
self.markAsRecentlyUsed(key)
self.trimToLimitIfNeeded()
}
}
public func removeObject(forKey key: Key) {
queue.async(flags: .barrier) {
self.store.removeValue(forKey: key)
self.keysByRecentUse.removeAll { $0 == key }
}
}
public func removeAll() {
queue.async(flags: .barrier) {
self.store.removeAll()
self.keysByRecentUse.removeAll()
}
}
/// 获取当前内容的快照(拷贝)
public var snapshot: [Key: Value] {
queue.sync { store }
}
private func markAsRecentlyUsed(_ key: Key) {
keysByRecentUse.removeAll { $0 == key }
keysByRecentUse.append(key)
}
private func trimToLimitIfNeeded() {
guard limit > 0 else { return }
while store.count > limit, let key = keysByRecentUse.first {
keysByRecentUse.removeFirst()
store.removeValue(forKey: key)
}
}
private func registerMemoryWarningObserver() {
#if canImport(UIKit) && !os(watchOS)
memoryWarningObserver = NotificationCenter.default.addObserver(
forName: UIApplication.didReceiveMemoryWarningNotification,
object: nil,
queue: nil
) { [weak self] _ in
self?.removeAll()
}
#endif
}
}