Add CachingDiskStorage
This commit is contained in:
parent
de67327f6d
commit
0b008489f7
|
@ -113,6 +113,7 @@
|
|||
D62D2426217ABF63005076CC /* UserActivityType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62D2425217ABF63005076CC /* UserActivityType.swift */; };
|
||||
D62FF04823D7CDD700909D6E /* AttributedStringHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62FF04723D7CDD700909D6E /* AttributedStringHelperTests.swift */; };
|
||||
D6311C5025B3765B00B27539 /* ImageDataCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6311C4F25B3765B00B27539 /* ImageDataCache.swift */; };
|
||||
D6311C5625B4CEA000B27539 /* CachingDiskStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6311C5525B4CEA000B27539 /* CachingDiskStorage.swift */; };
|
||||
D6333B372137838300CE884A /* AttributedString+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B362137838300CE884A /* AttributedString+Helpers.swift */; };
|
||||
D6333B792139AEFD00CE884A /* Date+TimeAgo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B782139AEFD00CE884A /* Date+TimeAgo.swift */; };
|
||||
D63569E023908A8D003DD353 /* StatusState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D60A4FFB238B726A008AC647 /* StatusState.swift */; };
|
||||
|
@ -470,6 +471,7 @@
|
|||
D62D2425217ABF63005076CC /* UserActivityType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserActivityType.swift; sourceTree = "<group>"; };
|
||||
D62FF04723D7CDD700909D6E /* AttributedStringHelperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedStringHelperTests.swift; sourceTree = "<group>"; };
|
||||
D6311C4F25B3765B00B27539 /* ImageDataCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageDataCache.swift; sourceTree = "<group>"; };
|
||||
D6311C5525B4CEA000B27539 /* CachingDiskStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CachingDiskStorage.swift; sourceTree = "<group>"; };
|
||||
D6333B362137838300CE884A /* AttributedString+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AttributedString+Helpers.swift"; sourceTree = "<group>"; };
|
||||
D6333B782139AEFD00CE884A /* Date+TimeAgo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+TimeAgo.swift"; sourceTree = "<group>"; };
|
||||
D63661BF2381C144004B9E16 /* PreferencesNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesNavigationController.swift; sourceTree = "<group>"; };
|
||||
|
@ -1496,6 +1498,7 @@
|
|||
D6F1F84C2193B56E00F5FE67 /* Cache.swift */,
|
||||
04DACE8D212CC7CC009840C4 /* ImageCache.swift */,
|
||||
D6311C4F25B3765B00B27539 /* ImageDataCache.swift */,
|
||||
D6311C5525B4CEA000B27539 /* CachingDiskStorage.swift */,
|
||||
);
|
||||
path = Caching;
|
||||
sourceTree = "<group>";
|
||||
|
@ -1874,6 +1877,7 @@
|
|||
D6A3BC8A2321F79B00FD64D5 /* AccountTableViewCell.swift in Sources */,
|
||||
D66A77BB233838DC0058F1EC /* UIFont+Traits.swift in Sources */,
|
||||
D68FEC4F232C5BC300C84F23 /* SegmentedPageViewController.swift in Sources */,
|
||||
D6311C5625B4CEA000B27539 /* CachingDiskStorage.swift in Sources */,
|
||||
D62275AA24F1E01C00B82A16 /* ComposeTextView.swift in Sources */,
|
||||
0450531F22B0097E00100BA2 /* Timline+UI.swift in Sources */,
|
||||
D667E5F52135BCD50057A976 /* ConversationTableViewController.swift in Sources */,
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
//
|
||||
// CachingDiskStorage.swift
|
||||
// Tusker
|
||||
//
|
||||
// Created by Shadowfacts on 1/17/21.
|
||||
// Copyright © 2021 Shadowfacts. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Cache
|
||||
|
||||
/// This class wraps a `DiskStorage` and maintains an in-memory cache of whether objects
|
||||
/// exist on disk to avoid unnecessary disk I/O when calling synchronous methods like
|
||||
/// `existsObject(forKey:)`.
|
||||
class CachingDiskStorage<T>: StorageAware {
|
||||
|
||||
private let storage: DiskStorage<T>
|
||||
private var files = [String: FileState]()
|
||||
|
||||
init(config: DiskConfig, transformer: Transformer<T>) throws {
|
||||
storage = try DiskStorage(config: config, transformer: transformer)
|
||||
}
|
||||
|
||||
private func state(for key: String) -> FileState {
|
||||
return files[key] ?? .unknown
|
||||
}
|
||||
|
||||
func entry(forKey key: String) throws -> Entry<T> {
|
||||
if state(for: key) == .doesNotExist {
|
||||
throw StorageError.notFound
|
||||
}
|
||||
return try storage.entry(forKey: key)
|
||||
}
|
||||
|
||||
func existsObject(forKey key: String) throws -> Bool {
|
||||
switch state(for: key) {
|
||||
case .unknown:
|
||||
return try storage.existsObject(forKey: key)
|
||||
case .exists:
|
||||
return true
|
||||
case .doesNotExist:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func removeObject(forKey key: String) throws {
|
||||
try storage.removeObject(forKey: key)
|
||||
files[key] = .doesNotExist
|
||||
}
|
||||
|
||||
func setObject(_ object: T, forKey key: String, expiry: Expiry? = nil) throws {
|
||||
try storage.setObject(object, forKey: key, expiry: expiry)
|
||||
files[key] = .exists
|
||||
}
|
||||
|
||||
func removeAll() throws {
|
||||
try storage.removeAll()
|
||||
files.removeAll()
|
||||
}
|
||||
|
||||
func removeExpiredObjects() throws {
|
||||
try storage.removeExpiredObjects()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension CachingDiskStorage {
|
||||
private enum FileState {
|
||||
case unknown, exists, doesNotExist
|
||||
}
|
||||
}
|
|
@ -37,10 +37,6 @@ class ImageCache {
|
|||
func get(_ url: URL, completion: ((Data?, UIImage?) -> Void)?) -> Request? {
|
||||
let key = url.absoluteString
|
||||
if !ImageCache.disableCaching,
|
||||
// todo: calling object(forKey: key) does disk I/O and this method is often called from the main thread
|
||||
// in performance sensitive paths. a nice optimization to DiskStorage would be adding an internal cache
|
||||
// of the state (unknown/exists/does not exist) of whether or not objects exist on disk so that the slow, disk I/O
|
||||
// path can be avoided most of the time
|
||||
let entry = try? cache.get(key) {
|
||||
if let completion = completion {
|
||||
backgroundQueue.async {
|
||||
|
|
|
@ -12,7 +12,7 @@ import Cache
|
|||
class ImageDataCache {
|
||||
|
||||
private let memory: MemoryStorage<Entry>
|
||||
private let disk: DiskStorage<Data>?
|
||||
private let disk: CachingDiskStorage<Data>?
|
||||
|
||||
private let storeOriginalDataInMemory: Bool
|
||||
private let desiredPixelSize: CGSize?
|
||||
|
@ -23,7 +23,7 @@ class ImageDataCache {
|
|||
|
||||
if let diskExpiry = diskExpiry {
|
||||
let diskConfig = DiskConfig(name: name, expiry: diskExpiry)
|
||||
self.disk = try! DiskStorage(config: diskConfig, transformer: TransformerFactory.forData())
|
||||
self.disk = try! CachingDiskStorage(config: diskConfig, transformer: TransformerFactory.forData())
|
||||
} else {
|
||||
self.disk = nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue