Przeglądaj źródła

Merge pull request #1196 from dstranz/feature/option-to-ignore-extendingExpiration

Add option to disable memory cache TTL extending
Wei Wang 6 lat temu
rodzic
commit
ba605dfb23

+ 1 - 1
Gemfile

@@ -4,4 +4,4 @@ source "https://rubygems.org"
 
 
 gem "fastlane"
 gem "fastlane"
 gem "jazzy"
 gem "jazzy"
-gem "cocoapods", "~> 1.7.1"
+gem "cocoapods", "~> 1.7.1"

+ 1 - 1
Podfile.lock

@@ -19,4 +19,4 @@ SPEC CHECKSUMS:
 
 
 PODFILE CHECKSUM: e1701972032ff803969f497525d8e4aa8929f695
 PODFILE CHECKSUM: e1701972032ff803969f497525d8e4aa8929f695
 
 
-COCOAPODS: 1.6.1
+COCOAPODS: 1.7.0

+ 1 - 1
Pods/Manifest.lock

@@ -19,4 +19,4 @@ SPEC CHECKSUMS:
 
 
 PODFILE CHECKSUM: e1701972032ff803969f497525d8e4aa8929f695
 PODFILE CHECKSUM: e1701972032ff803969f497525d8e4aa8929f695
 
 
-COCOAPODS: 1.6.1
+COCOAPODS: 1.7.0

Plik diff jest za duży
+ 460 - 459
Pods/Pods.xcodeproj/project.pbxproj


+ 1 - 5
Sources/Cache/ImageCache.swift

@@ -525,11 +525,7 @@ open class ImageCache {
         options: KingfisherParsedOptionsInfo) -> Image?
         options: KingfisherParsedOptionsInfo) -> Image?
     {
     {
         let computedKey = key.computedKey(with: options.processor.identifier)
         let computedKey = key.computedKey(with: options.processor.identifier)
-        do {
-            return try memoryStorage.value(forKey: computedKey)
-        } catch {
-            return nil
-        }
+        return memoryStorage.value(forKey: computedKey, extendingExpiration: options.memoryCacheAccessExtendingExpiration)
     }
     }
 
 
     /// Gets an image for a given key from the memory storage.
     /// Gets an image for a given key from the memory storage.

+ 24 - 13
Sources/Cache/MemoryStorage.swift

@@ -121,26 +121,30 @@ public enum MemoryStorage {
             storage.setObject(object, forKey: key as NSString, cost: value.cacheCost)
             storage.setObject(object, forKey: key as NSString, cost: value.cacheCost)
             keys.insert(key)
             keys.insert(key)
         }
         }
-
-        // Use this when you actually access the memory cached item.
-        // This will extend the expired data for the accessed item.
-        func value(forKey key: String) throws -> T? {
-            return value(forKey: key, extendingExpiration: true)
-        }
-
-        func value(forKey key: String, extendingExpiration: Bool) -> T? {
+        
+        /// Use this when you actually access the memory cached item.
+        /// By default, this will extend the expired data for the accessed item.
+        ///
+        /// - Parameters:
+        ///   - key: Cache Key
+        ///   - extendingExpiration: expiration value to extend item expiration time:
+        ///     * .none: The item expires after the original time, without extending after access.
+        ///     * .cacheTime: The item expiration extends by the original cache time after each access.
+        ///     * .expirationTime: The item expiration extends by the provided time after each access.
+        /// - Returns: cached object or nil
+        func value(forKey key: String, extendingExpiration: ExpirationExtending = .cacheTime) -> T? {
             guard let object = storage.object(forKey: key as NSString) else {
             guard let object = storage.object(forKey: key as NSString) else {
                 return nil
                 return nil
             }
             }
             if object.expired {
             if object.expired {
                 return nil
                 return nil
             }
             }
-            if extendingExpiration { object.extendExpiration() }
+            object.extendExpiration(extendingExpiration)
             return object.value
             return object.value
         }
         }
 
 
         func isCached(forKey key: String) -> Bool {
         func isCached(forKey key: String) -> Bool {
-            guard let _ = value(forKey: key, extendingExpiration: false) else {
+            guard let _ = value(forKey: key, extendingExpiration: .none) else {
                 return false
                 return false
             }
             }
             return true
             return true
@@ -219,9 +223,16 @@ extension MemoryStorage {
             
             
             self.estimatedExpiration = expiration.estimatedExpirationSinceNow
             self.estimatedExpiration = expiration.estimatedExpirationSinceNow
         }
         }
-        
-        func extendExpiration() {
-            self.estimatedExpiration = expiration.estimatedExpirationSinceNow
+
+        func extendExpiration(_ extendingExpiration: ExpirationExtending = .cacheTime) {
+            switch extendingExpiration {
+            case .none:
+                return
+            case .cacheTime:
+                self.estimatedExpiration = expiration.estimatedExpirationSinceNow
+            case .expirationTime(let expirationTime):
+                self.estimatedExpiration = expirationTime.estimatedExpirationSinceNow
+            }
         }
         }
         
         
         var expired: Bool {
         var expired: Bool {

+ 14 - 0
Sources/Cache/Storage.swift

@@ -81,6 +81,20 @@ public enum StorageExpiration {
     }
     }
 }
 }
 
 
+/// Represents the expiration extending strategy used in storage to after access.
+///
+/// - none: The item expires after the original time, without extending after access.
+/// - cacheTime: The item expiration extends by the original cache time after each access.
+/// - expirationTime: The item expiration extends by the provided time after each access.
+public enum ExpirationExtending {
+    /// The item expires after the original time, without extending after access.
+    case none
+    /// The item expiration extends by the original cache time after each access.
+    case cacheTime
+    /// The item expiration extends by the provided time after each access.
+    case expirationTime(_ expiration: StorageExpiration)
+}
+
 /// Represents types which cost in memory can be calculated.
 /// Represents types which cost in memory can be calculated.
 public protocol CacheCostCalculable {
 public protocol CacheCostCalculable {
     var cacheCost: Int { get }
     var cacheCost: Int { get }

+ 7 - 0
Sources/General/KingfisherOptionsInfo.swift

@@ -203,6 +203,11 @@ public enum KingfisherOptionsInfoItem {
     /// value to overwrite the config setting for this caching item.
     /// value to overwrite the config setting for this caching item.
     case memoryCacheExpiration(StorageExpiration)
     case memoryCacheExpiration(StorageExpiration)
     
     
+    /// The expiration extending setting for memory cache. The item expiration time will be incremented by this value after access.
+    /// By default, the underlying `MemoryStorage.Backend` uses the initial cache expiration as extending value: .cacheTime.
+    /// To disable extending option at all add memoryCacheAccessExtendingExpiration(.none) to options.
+    case memoryCacheAccessExtendingExpiration(ExpirationExtending)
+    
     /// The expiration setting for memory cache. By default, the underlying `DiskStorage.Backend` uses the
     /// The expiration setting for memory cache. By default, the underlying `DiskStorage.Backend` uses the
     /// expiration in its config for all items. If set, the `DiskStorage.Backend` will use this associated
     /// expiration in its config for all items. If set, the `DiskStorage.Backend` will use this associated
     /// value to overwrite the config setting for this caching item.
     /// value to overwrite the config setting for this caching item.
@@ -252,6 +257,7 @@ public struct KingfisherParsedOptionsInfo {
     public var alsoPrefetchToMemory = false
     public var alsoPrefetchToMemory = false
     public var loadDiskFileSynchronously = false
     public var loadDiskFileSynchronously = false
     public var memoryCacheExpiration: StorageExpiration? = nil
     public var memoryCacheExpiration: StorageExpiration? = nil
+    public var memoryCacheAccessExtendingExpiration: ExpirationExtending = .cacheTime
     public var diskCacheExpiration: StorageExpiration? = nil
     public var diskCacheExpiration: StorageExpiration? = nil
     public var processingQueue: CallbackQueue? = nil
     public var processingQueue: CallbackQueue? = nil
     public var progressiveJPEG: ImageProgressive? = nil
     public var progressiveJPEG: ImageProgressive? = nil
@@ -290,6 +296,7 @@ public struct KingfisherParsedOptionsInfo {
             case .loadDiskFileSynchronously: loadDiskFileSynchronously = true
             case .loadDiskFileSynchronously: loadDiskFileSynchronously = true
             case .callbackDispatchQueue(let value): callbackQueue = value.map { .dispatch($0) } ?? .mainCurrentOrAsync
             case .callbackDispatchQueue(let value): callbackQueue = value.map { .dispatch($0) } ?? .mainCurrentOrAsync
             case .memoryCacheExpiration(let expiration): memoryCacheExpiration = expiration
             case .memoryCacheExpiration(let expiration): memoryCacheExpiration = expiration
+            case .memoryCacheAccessExtendingExpiration(let expirationExtending): memoryCacheAccessExtendingExpiration = expirationExtending
             case .diskCacheExpiration(let expiration): diskCacheExpiration = expiration
             case .diskCacheExpiration(let expiration): diskCacheExpiration = expiration
             case .processingQueue(let queue): processingQueue = queue
             case .processingQueue(let queue): processingQueue = queue
             case .progressiveJPEG(let value): progressiveJPEG = value
             case .progressiveJPEG(let value): progressiveJPEG = value

+ 69 - 1
Tests/KingfisherTests/ImageViewExtensionTests.swift

@@ -335,7 +335,7 @@ class ImageViewExtensionTests: XCTestCase {
         waitForExpectations(timeout: 3, handler: nil)
         waitForExpectations(timeout: 3, handler: nil)
     }
     }
     
     
-    func testCacnelImageTask() {
+    func testCancelImageTask() {
         let exp = expectation(description: #function)
         let exp = expectation(description: #function)
         let url = testURLs[0]
         let url = testURLs[0]
         let stub = delayedStub(url, data: testImageData)
         let stub = delayedStub(url, data: testImageData)
@@ -652,6 +652,74 @@ class ImageViewExtensionTests: XCTestCase {
         group.notify(queue: .main) { exp.fulfill() }
         group.notify(queue: .main) { exp.fulfill() }
         waitForExpectations(timeout: 3, handler: nil)
         waitForExpectations(timeout: 3, handler: nil)
     }
     }
+    
+    func testImageCacheExtendingExpirationTask() {
+        let exp = expectation(description: #function)
+        let url = testURLs[0]
+        stub(url, data: testImageData)
+        
+        let options: KingfisherOptionsInfo = [.cacheMemoryOnly, .memoryCacheExpiration(.seconds(1)), .memoryCacheAccessExtendingExpiration(.expirationTime(.seconds(100)))]
+       
+        imageView.kf.setImage(with: url, options: options) { result in
+            XCTAssertNotNil(result.value?.image)
+            XCTAssertTrue(result.value!.cacheType == .none)
+            
+            let cacheKey = result.value!.source.cacheKey as NSString
+            let expirationTime1 = ImageCache.default.memoryStorage.storage.object(forKey: cacheKey)?.estimatedExpiration
+            XCTAssertNotNil(expirationTime1)
+            
+            delay(0.1, block: {
+                self.imageView.kf.setImage(with: url, options: options) { result in
+                    XCTAssertNotNil(result.value?.image)
+                    XCTAssertTrue(result.value!.cacheType == .memory)
+                    
+                    let expirationTime2 = ImageCache.default.memoryStorage.storage.object(forKey: cacheKey)?.estimatedExpiration
+                    
+                    XCTAssertNotNil(expirationTime2)
+                    XCTAssertNotEqual(expirationTime1, expirationTime2)
+                    XCTAssert(expirationTime1!.isPast(referenceDate: expirationTime2!))
+                    XCTAssertGreaterThan(expirationTime2!.timeIntervalSince(expirationTime1!), 10)
+                    
+                    exp.fulfill()
+                }
+            })
+        }
+
+        waitForExpectations(timeout: 3, handler: nil)
+    }
+    
+    func testImageCacheNotExtendingExpirationTask() {
+        let exp = expectation(description: #function)
+        let url = testURLs[0]
+        stub(url, data: testImageData)
+        
+        let options: KingfisherOptionsInfo = [.cacheMemoryOnly, .memoryCacheExpiration(.seconds(1)), .memoryCacheAccessExtendingExpiration(.none)]
+  
+        imageView.kf.setImage(with: url, options: options) { result in
+            XCTAssertNotNil(result.value?.image)
+            XCTAssertTrue(result.value!.cacheType == .none)
+            
+            let cacheKey = result.value!.source.cacheKey as NSString
+            let expirationTime1 = ImageCache.default.memoryStorage.storage.object(forKey: cacheKey)?.estimatedExpiration
+            XCTAssertNotNil(expirationTime1)
+            
+            delay(0.1, block: {
+                self.imageView.kf.setImage(with: url, options: options) { result in
+                    XCTAssertNotNil(result.value?.image)
+                    XCTAssertTrue(result.value!.cacheType == .memory)
+                    
+                    let expirationTime2 = ImageCache.default.memoryStorage.storage.object(forKey: cacheKey)?.estimatedExpiration
+                    
+                    XCTAssertNotNil(expirationTime2)
+                    XCTAssertEqual(expirationTime1, expirationTime2)
+                    
+                    exp.fulfill()
+                }
+            })
+        }
+        
+        waitForExpectations(timeout: 3, handler: nil)
+    }
 }
 }
 
 
 extension View: Placeholder {}
 extension View: Placeholder {}

+ 51 - 0
Tests/KingfisherTests/MemoryStorageTests.swift

@@ -138,6 +138,57 @@ class MemoryStorageTests: XCTestCase {
         }
         }
         waitForExpectations(timeout: 3, handler: nil)
         waitForExpectations(timeout: 3, handler: nil)
     }
     }
+    
+    func testStoreWithExpirationExtending() {
+        let exp = expectation(description: #function)
+        
+        XCTAssertFalse(storage.isCached(forKey: "1"))
+        try! storage.store(value: 1, forKey: "1", expiration: .seconds(1))
+        XCTAssertTrue(storage.isCached(forKey: "1"))
+        
+        delay(0.1) {
+            let expirationDate1 = self.storage.storage.object(forKey: "1")?.estimatedExpiration
+            XCTAssertNotNil(expirationDate1)
+            
+            // Request for the object to extend it's expiration date
+            let obj = self.storage.value(forKey: "1", extendingExpiration: .expirationTime(.seconds(5)))
+            XCTAssertNotNil(obj)
+            
+            let expirationDate2 = self.storage.storage.object(forKey: "1")?.estimatedExpiration
+            XCTAssertNotNil(expirationDate2)
+            
+            XCTAssertNotEqual(expirationDate1!, expirationDate2!)
+            XCTAssert(expirationDate1!.isPast(referenceDate: expirationDate2!))
+            exp.fulfill()
+        }
+        
+        waitForExpectations(timeout: 3, handler: nil)
+    }
+    
+    func testStoreWithExpirationNotExtending() {
+        let exp = expectation(description: #function)
+        
+        XCTAssertFalse(storage.isCached(forKey: "1"))
+        try! storage.store(value: 1, forKey: "1", expiration: .seconds(1))
+        XCTAssertTrue(storage.isCached(forKey: "1"))
+        
+        delay(0.1) {
+            let expirationDate1 = self.storage.storage.object(forKey: "1")?.estimatedExpiration
+            XCTAssertNotNil(expirationDate1)
+            
+            // Request for the object to extend it's expiration date
+            let obj = self.storage.value(forKey: "1", extendingExpiration: .none)
+            XCTAssertNotNil(obj)
+            
+            let expirationDate2 = self.storage.storage.object(forKey: "1")?.estimatedExpiration
+            XCTAssertNotNil(expirationDate2)
+            
+            XCTAssertEqual(expirationDate1, expirationDate2)
+            exp.fulfill()
+        }
+        
+        waitForExpectations(timeout: 3, handler: nil)
+    }
 
 
     func testRemoveExpired() {
     func testRemoveExpired() {
         let exp = expectation(description: #function)
         let exp = expectation(description: #function)

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików