1- import Foundation
1+ #if canImport(UIKit)
2+ import UIKit
3+ #elseif canImport(AppKit) && !targetEnvironment(macCatalyst)
4+ import AppKit
5+ #endif
26
37public class MemoryStorage < Key: Hashable , Value> : StorageAware {
48 final class WrappedKey : NSObject {
@@ -22,11 +26,39 @@ public class MemoryStorage<Key: Hashable, Value>: StorageAware {
2226 fileprivate var keys = Set < Key > ( )
2327 /// Configuration
2428 fileprivate let config : MemoryConfig
29+
30+ /// The closure to be called when the key has been removed
31+ public var onRemove : ( ( Key ) -> Void ) ?
32+
33+ public var didEnterBackgroundObserver : NSObjectProtocol ?
2534
2635 public init ( config: MemoryConfig ) {
2736 self . config = config
2837 self . cache. countLimit = Int ( config. countLimit)
2938 self . cache. totalCostLimit = Int ( config. totalCostLimit)
39+ applyExpiratonMode ( self . config. expirationMode)
40+ }
41+
42+ public func applyExpiratonMode( _ expirationMode: ExpirationMode ) {
43+ if let didEnterBackgroundObserver = didEnterBackgroundObserver {
44+ NotificationCenter . default. removeObserver ( didEnterBackgroundObserver)
45+ }
46+ if expirationMode == . auto {
47+ didEnterBackgroundObserver =
48+ NotificationCenter . default. addObserver ( forName: UIApplication . didEnterBackgroundNotification,
49+ object: nil ,
50+ queue: nil )
51+ { [ weak self] _ in
52+ guard let `self` = self else { return }
53+ self . removeExpiredObjects ( )
54+ }
55+ }
56+ }
57+
58+ deinit {
59+ if let didEnterBackgroundObserver = didEnterBackgroundObserver {
60+ NotificationCenter . default. removeObserver ( didEnterBackgroundObserver)
61+ }
3062 }
3163}
3264
@@ -41,8 +73,10 @@ extension MemoryStorage {
4173
4274 public func setObject( _ object: Value , forKey key: Key , expiry: Expiry ? = nil ) {
4375 let capsule = MemoryCapsule ( value: object, expiry: . date( expiry? . date ?? config. expiry. date) )
44- let const = MemoryLayout . size ( ofValue: capsule)
45- cache. setObject ( capsule, forKey: WrappedKey ( key) , cost: const)
76+
77+ /// MemoryLayout.size(ofValue:) return the contiguous memory footprint of the given instance , so cost is always MemoryCapsule size (8 bytes)
78+ let cost = MemoryLayout . size ( ofValue: capsule)
79+ cache. setObject ( capsule, forKey: WrappedKey ( key) , cost: cost)
4680 keys. insert ( key)
4781 }
4882
@@ -67,6 +101,7 @@ extension MemoryStorage {
67101 public func removeObject( forKey key: Key ) {
68102 cache. removeObject ( forKey: WrappedKey ( key) )
69103 keys. remove ( key)
104+ onRemove ? ( key)
70105 }
71106
72107 public func entry( forKey key: Key ) throws -> Entry < Value > {
0 commit comments