From 137bb3fac03de01a8bef8ecb02b19daf05af2121 Mon Sep 17 00:00:00 2001 From: Sami Samhuri Date: Wed, 10 Aug 2016 23:59:07 -0700 Subject: [PATCH] replace random eviction with LRU eviction Also took the opportunity to make the API conform to Swift 3 convention. --- CacheCreek-iOS/CacheCreek.h | 19 ++ .../Info.plist | 0 CacheCreek-tvOS/CacheCreek.h | 19 ++ .../Info.plist | 0 CacheCreek.podspec | 16 ++ .../project.pbxproj | 162 +++++++++--------- .../contents.xcworkspacedata | 2 +- .../xcschemes/CacheCreek-iOS.xcscheme | 35 ++-- .../xcschemes/CacheCreek-tvOS.xcscheme | 20 +-- .../Info.plist | 0 .../LRUCacheTests.swift | 132 +++++++------- CacheIsKing-iOS/CacheIsKing.h | 19 -- CacheIsKing-tvOS/CacheIsKing.h | 19 -- CacheIsKing.podspec | 16 -- README.md | 38 ++-- Source/AnyKey.swift | 2 +- Source/DoublyLinkedList.swift | 100 +++++++++++ Source/{KingCache.swift => LRUCache.swift} | 93 +++++----- 18 files changed, 406 insertions(+), 286 deletions(-) create mode 100644 CacheCreek-iOS/CacheCreek.h rename {CacheIsKing-iOS => CacheCreek-iOS}/Info.plist (100%) create mode 100644 CacheCreek-tvOS/CacheCreek.h rename {CacheIsKing-tvOS => CacheCreek-tvOS}/Info.plist (100%) create mode 100644 CacheCreek.podspec rename {CacheIsKing.xcodeproj => CacheCreek.xcodeproj}/project.pbxproj (75%) rename {CacheIsKing.xcodeproj => CacheCreek.xcodeproj}/project.xcworkspace/contents.xcworkspacedata (58%) rename CacheIsKing.xcodeproj/xcshareddata/xcschemes/CacheIsKing-iOS.xcscheme => CacheCreek.xcodeproj/xcshareddata/xcschemes/CacheCreek-iOS.xcscheme (74%) rename CacheIsKing.xcodeproj/xcshareddata/xcschemes/CacheIsKing-tvOS.xcscheme => CacheCreek.xcodeproj/xcshareddata/xcschemes/CacheCreek-tvOS.xcscheme (81%) rename {CacheIsKingTests => CacheCreekTests}/Info.plist (100%) rename CacheIsKingTests/CacheIsKingTests.swift => CacheCreekTests/LRUCacheTests.swift (57%) delete mode 100644 CacheIsKing-iOS/CacheIsKing.h delete mode 100644 CacheIsKing-tvOS/CacheIsKing.h delete mode 100644 CacheIsKing.podspec create mode 100644 Source/DoublyLinkedList.swift rename Source/{KingCache.swift => LRUCache.swift} (59%) diff --git a/CacheCreek-iOS/CacheCreek.h b/CacheCreek-iOS/CacheCreek.h new file mode 100644 index 0000000..b49868a --- /dev/null +++ b/CacheCreek-iOS/CacheCreek.h @@ -0,0 +1,19 @@ +// +// CacheCreek-iOS.h +// CacheCreek-iOS +// +// Created by Christopher Luu on 1/26/16. +// +// + +#import + +//! Project version number for CacheCreek-iOS. +FOUNDATION_EXPORT double CacheCreekVersionNumber; + +//! Project version string for CacheCreek-iOS. +FOUNDATION_EXPORT const unsigned char CacheCreekVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/CacheIsKing-iOS/Info.plist b/CacheCreek-iOS/Info.plist similarity index 100% rename from CacheIsKing-iOS/Info.plist rename to CacheCreek-iOS/Info.plist diff --git a/CacheCreek-tvOS/CacheCreek.h b/CacheCreek-tvOS/CacheCreek.h new file mode 100644 index 0000000..18ba6ca --- /dev/null +++ b/CacheCreek-tvOS/CacheCreek.h @@ -0,0 +1,19 @@ +// +// CacheCreek-tvOS.h +// CacheCreek-tvOS +// +// Created by Christopher Luu on 1/26/16. +// +// + +#import + +//! Project version number for CacheCreek-tvOS. +FOUNDATION_EXPORT double CacheCreeksVersionNumber; + +//! Project version string for CacheCreek-tvOS. +FOUNDATION_EXPORT const unsigned char CacheCreekVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/CacheIsKing-tvOS/Info.plist b/CacheCreek-tvOS/Info.plist similarity index 100% rename from CacheIsKing-tvOS/Info.plist rename to CacheCreek-tvOS/Info.plist diff --git a/CacheCreek.podspec b/CacheCreek.podspec new file mode 100644 index 0000000..c27ac9a --- /dev/null +++ b/CacheCreek.podspec @@ -0,0 +1,16 @@ +Pod::Spec.new do |s| + s.name = 'CacheCreek' + s.version = '0.1.0' + s.license = 'MIT' + s.summary = 'An LRU cache that can hold anything, including native Swift types.' + s.homepage = 'https://github.com/samsonjs/CacheCreek' + s.authors = { 'Christopher Luu' => 'nuudles@gmail.com', 'Sami Samhuri' => 'sami@samhuri.net' } + s.source = { :git => 'https://github.com/samsonjs/CacheCreek.git', :tag => s.version } + + s.ios.deployment_target = '8.0' + s.tvos.deployment_target = '9.0' + + s.source_files = 'Source/*.swift' + + s.requires_arc = true +end diff --git a/CacheIsKing.xcodeproj/project.pbxproj b/CacheCreek.xcodeproj/project.pbxproj similarity index 75% rename from CacheIsKing.xcodeproj/project.pbxproj rename to CacheCreek.xcodeproj/project.pbxproj index 6108eb2..f999bb5 100644 --- a/CacheIsKing.xcodeproj/project.pbxproj +++ b/CacheCreek.xcodeproj/project.pbxproj @@ -7,14 +7,16 @@ objects = { /* Begin PBXBuildFile section */ - 30D75C271C57CE1400F4E62D /* CacheIsKing.h in Headers */ = {isa = PBXBuildFile; fileRef = 30D75C261C57CE1400F4E62D /* CacheIsKing.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 30D75C2E1C57CEC500F4E62D /* KingCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30D75C2D1C57CEC500F4E62D /* KingCache.swift */; }; - 30D75C371C57D17400F4E62D /* CacheIsKing.h in Headers */ = {isa = PBXBuildFile; fileRef = 30D75C361C57D17400F4E62D /* CacheIsKing.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 30D75C3C1C57D1A300F4E62D /* KingCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30D75C2D1C57CEC500F4E62D /* KingCache.swift */; }; - 30D75C441C57D2CA00F4E62D /* CacheIsKingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30D75C431C57D2CA00F4E62D /* CacheIsKingTests.swift */; }; - 30D75C461C57D2CA00F4E62D /* CacheIsKing.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 30D75C231C57CE1400F4E62D /* CacheIsKing.framework */; }; + 30D75C271C57CE1400F4E62D /* CacheCreek.h in Headers */ = {isa = PBXBuildFile; fileRef = 30D75C261C57CE1400F4E62D /* CacheCreek.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 30D75C2E1C57CEC500F4E62D /* LRUCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30D75C2D1C57CEC500F4E62D /* LRUCache.swift */; }; + 30D75C371C57D17400F4E62D /* CacheCreek.h in Headers */ = {isa = PBXBuildFile; fileRef = 30D75C361C57D17400F4E62D /* CacheCreek.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 30D75C3C1C57D1A300F4E62D /* LRUCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30D75C2D1C57CEC500F4E62D /* LRUCache.swift */; }; + 30D75C461C57D2CA00F4E62D /* CacheCreek.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 30D75C231C57CE1400F4E62D /* CacheCreek.framework */; }; 30D75C4D1C57F1C800F4E62D /* AnyKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30D75C4C1C57F1C800F4E62D /* AnyKey.swift */; }; 30D75C4E1C58033B00F4E62D /* AnyKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30D75C4C1C57F1C800F4E62D /* AnyKey.swift */; }; + 7BC24BAC1D5C432200B1C208 /* LRUCacheTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BC24BAB1D5C432200B1C208 /* LRUCacheTests.swift */; }; + 7BC24BAF1D5C54E300B1C208 /* DoublyLinkedList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BC24BAD1D5C462100B1C208 /* DoublyLinkedList.swift */; }; + 7BC24BB01D5C54E300B1C208 /* DoublyLinkedList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BC24BAD1D5C462100B1C208 /* DoublyLinkedList.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -23,22 +25,23 @@ containerPortal = 30D75C181C57CD6100F4E62D /* Project object */; proxyType = 1; remoteGlobalIDString = 30D75C221C57CE1400F4E62D; - remoteInfo = "CacheIsKing-iOS"; + remoteInfo = "CacheCreek-iOS"; }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - 30D75C231C57CE1400F4E62D /* CacheIsKing.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CacheIsKing.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 30D75C261C57CE1400F4E62D /* CacheIsKing.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CacheIsKing.h; sourceTree = ""; }; + 30D75C231C57CE1400F4E62D /* CacheCreek.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CacheCreek.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 30D75C261C57CE1400F4E62D /* CacheCreek.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CacheCreek.h; sourceTree = ""; }; 30D75C281C57CE1400F4E62D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 30D75C2D1C57CEC500F4E62D /* KingCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KingCache.swift; sourceTree = ""; }; - 30D75C341C57D17400F4E62D /* CacheIsKing.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CacheIsKing.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 30D75C361C57D17400F4E62D /* CacheIsKing.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CacheIsKing.h; sourceTree = ""; }; + 30D75C2D1C57CEC500F4E62D /* LRUCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LRUCache.swift; sourceTree = ""; }; + 30D75C341C57D17400F4E62D /* CacheCreek.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CacheCreek.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 30D75C361C57D17400F4E62D /* CacheCreek.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CacheCreek.h; sourceTree = ""; }; 30D75C381C57D17400F4E62D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 30D75C411C57D2CA00F4E62D /* CacheIsKingTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CacheIsKingTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 30D75C431C57D2CA00F4E62D /* CacheIsKingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CacheIsKingTests.swift; sourceTree = ""; }; + 30D75C411C57D2CA00F4E62D /* CacheCreekTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CacheCreekTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 30D75C451C57D2CA00F4E62D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 30D75C4C1C57F1C800F4E62D /* AnyKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnyKey.swift; sourceTree = ""; }; + 7BC24BAB1D5C432200B1C208 /* LRUCacheTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LRUCacheTests.swift; sourceTree = ""; }; + 7BC24BAD1D5C462100B1C208 /* DoublyLinkedList.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DoublyLinkedList.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -60,7 +63,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 30D75C461C57D2CA00F4E62D /* CacheIsKing.framework in Frameworks */, + 30D75C461C57D2CA00F4E62D /* CacheCreek.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -71,9 +74,9 @@ isa = PBXGroup; children = ( 30D75C2C1C57CEB600F4E62D /* Source */, - 30D75C251C57CE1400F4E62D /* CacheIsKing-iOS */, - 30D75C351C57D17400F4E62D /* CacheIsKing-tvOS */, - 30D75C421C57D2CA00F4E62D /* CacheIsKingTests */, + 30D75C251C57CE1400F4E62D /* CacheCreek-iOS */, + 30D75C351C57D17400F4E62D /* CacheCreek-tvOS */, + 30D75C421C57D2CA00F4E62D /* CacheCreekTests */, 30D75C241C57CE1400F4E62D /* Products */, ); sourceTree = ""; @@ -81,47 +84,48 @@ 30D75C241C57CE1400F4E62D /* Products */ = { isa = PBXGroup; children = ( - 30D75C231C57CE1400F4E62D /* CacheIsKing.framework */, - 30D75C341C57D17400F4E62D /* CacheIsKing.framework */, - 30D75C411C57D2CA00F4E62D /* CacheIsKingTests.xctest */, + 30D75C231C57CE1400F4E62D /* CacheCreek.framework */, + 30D75C341C57D17400F4E62D /* CacheCreek.framework */, + 30D75C411C57D2CA00F4E62D /* CacheCreekTests.xctest */, ); name = Products; sourceTree = ""; }; - 30D75C251C57CE1400F4E62D /* CacheIsKing-iOS */ = { + 30D75C251C57CE1400F4E62D /* CacheCreek-iOS */ = { isa = PBXGroup; children = ( - 30D75C261C57CE1400F4E62D /* CacheIsKing.h */, + 30D75C261C57CE1400F4E62D /* CacheCreek.h */, 30D75C281C57CE1400F4E62D /* Info.plist */, ); - path = "CacheIsKing-iOS"; + path = "CacheCreek-iOS"; sourceTree = ""; }; 30D75C2C1C57CEB600F4E62D /* Source */ = { isa = PBXGroup; children = ( - 30D75C2D1C57CEC500F4E62D /* KingCache.swift */, 30D75C4C1C57F1C800F4E62D /* AnyKey.swift */, + 7BC24BAD1D5C462100B1C208 /* DoublyLinkedList.swift */, + 30D75C2D1C57CEC500F4E62D /* LRUCache.swift */, ); path = Source; sourceTree = ""; }; - 30D75C351C57D17400F4E62D /* CacheIsKing-tvOS */ = { + 30D75C351C57D17400F4E62D /* CacheCreek-tvOS */ = { isa = PBXGroup; children = ( - 30D75C361C57D17400F4E62D /* CacheIsKing.h */, + 30D75C361C57D17400F4E62D /* CacheCreek.h */, 30D75C381C57D17400F4E62D /* Info.plist */, ); - path = "CacheIsKing-tvOS"; + path = "CacheCreek-tvOS"; sourceTree = ""; }; - 30D75C421C57D2CA00F4E62D /* CacheIsKingTests */ = { + 30D75C421C57D2CA00F4E62D /* CacheCreekTests */ = { isa = PBXGroup; children = ( - 30D75C431C57D2CA00F4E62D /* CacheIsKingTests.swift */, + 7BC24BAB1D5C432200B1C208 /* LRUCacheTests.swift */, 30D75C451C57D2CA00F4E62D /* Info.plist */, ); - path = CacheIsKingTests; + path = CacheCreekTests; sourceTree = ""; }; /* End PBXGroup section */ @@ -131,7 +135,7 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 30D75C271C57CE1400F4E62D /* CacheIsKing.h in Headers */, + 30D75C271C57CE1400F4E62D /* CacheCreek.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -139,16 +143,16 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 30D75C371C57D17400F4E62D /* CacheIsKing.h in Headers */, + 30D75C371C57D17400F4E62D /* CacheCreek.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ - 30D75C221C57CE1400F4E62D /* CacheIsKing-iOS */ = { + 30D75C221C57CE1400F4E62D /* CacheCreek-iOS */ = { isa = PBXNativeTarget; - buildConfigurationList = 30D75C291C57CE1400F4E62D /* Build configuration list for PBXNativeTarget "CacheIsKing-iOS" */; + buildConfigurationList = 30D75C291C57CE1400F4E62D /* Build configuration list for PBXNativeTarget "CacheCreek-iOS" */; buildPhases = ( 30D75C1E1C57CE1400F4E62D /* Sources */, 30D75C1F1C57CE1400F4E62D /* Frameworks */, @@ -159,14 +163,14 @@ ); dependencies = ( ); - name = "CacheIsKing-iOS"; - productName = "CacheIsKing-iOS"; - productReference = 30D75C231C57CE1400F4E62D /* CacheIsKing.framework */; + name = "CacheCreek-iOS"; + productName = "CacheCreek-iOS"; + productReference = 30D75C231C57CE1400F4E62D /* CacheCreek.framework */; productType = "com.apple.product-type.framework"; }; - 30D75C331C57D17400F4E62D /* CacheIsKing-tvOS */ = { + 30D75C331C57D17400F4E62D /* CacheCreek-tvOS */ = { isa = PBXNativeTarget; - buildConfigurationList = 30D75C391C57D17400F4E62D /* Build configuration list for PBXNativeTarget "CacheIsKing-tvOS" */; + buildConfigurationList = 30D75C391C57D17400F4E62D /* Build configuration list for PBXNativeTarget "CacheCreek-tvOS" */; buildPhases = ( 30D75C2F1C57D17400F4E62D /* Sources */, 30D75C301C57D17400F4E62D /* Frameworks */, @@ -177,14 +181,14 @@ ); dependencies = ( ); - name = "CacheIsKing-tvOS"; - productName = "CacheIsKing-tvOS"; - productReference = 30D75C341C57D17400F4E62D /* CacheIsKing.framework */; + name = "CacheCreek-tvOS"; + productName = "CacheCreek-tvOS"; + productReference = 30D75C341C57D17400F4E62D /* CacheCreek.framework */; productType = "com.apple.product-type.framework"; }; - 30D75C401C57D2CA00F4E62D /* CacheIsKingTests */ = { + 30D75C401C57D2CA00F4E62D /* CacheCreekTests */ = { isa = PBXNativeTarget; - buildConfigurationList = 30D75C491C57D2CA00F4E62D /* Build configuration list for PBXNativeTarget "CacheIsKingTests" */; + buildConfigurationList = 30D75C491C57D2CA00F4E62D /* Build configuration list for PBXNativeTarget "CacheCreekTests" */; buildPhases = ( 30D75C3D1C57D2CA00F4E62D /* Sources */, 30D75C3E1C57D2CA00F4E62D /* Frameworks */, @@ -195,9 +199,9 @@ dependencies = ( 30D75C481C57D2CA00F4E62D /* PBXTargetDependency */, ); - name = CacheIsKingTests; - productName = CacheIsKingTests; - productReference = 30D75C411C57D2CA00F4E62D /* CacheIsKingTests.xctest */; + name = CacheCreekTests; + productName = CacheCreekTests; + productReference = 30D75C411C57D2CA00F4E62D /* CacheCreekTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; /* End PBXNativeTarget section */ @@ -207,7 +211,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0720; - LastUpgradeCheck = 0720; + LastUpgradeCheck = 0730; TargetAttributes = { 30D75C221C57CE1400F4E62D = { CreatedOnToolsVersion = 7.2; @@ -220,7 +224,7 @@ }; }; }; - buildConfigurationList = 30D75C1B1C57CD6100F4E62D /* Build configuration list for PBXProject "CacheIsKing" */; + buildConfigurationList = 30D75C1B1C57CD6100F4E62D /* Build configuration list for PBXProject "CacheCreek" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; @@ -232,9 +236,9 @@ projectDirPath = ""; projectRoot = ""; targets = ( - 30D75C221C57CE1400F4E62D /* CacheIsKing-iOS */, - 30D75C331C57D17400F4E62D /* CacheIsKing-tvOS */, - 30D75C401C57D2CA00F4E62D /* CacheIsKingTests */, + 30D75C221C57CE1400F4E62D /* CacheCreek-iOS */, + 30D75C331C57D17400F4E62D /* CacheCreek-tvOS */, + 30D75C401C57D2CA00F4E62D /* CacheCreekTests */, ); }; /* End PBXProject section */ @@ -269,7 +273,8 @@ buildActionMask = 2147483647; files = ( 30D75C4D1C57F1C800F4E62D /* AnyKey.swift in Sources */, - 30D75C2E1C57CEC500F4E62D /* KingCache.swift in Sources */, + 7BC24BAF1D5C54E300B1C208 /* DoublyLinkedList.swift in Sources */, + 30D75C2E1C57CEC500F4E62D /* LRUCache.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -278,7 +283,8 @@ buildActionMask = 2147483647; files = ( 30D75C4E1C58033B00F4E62D /* AnyKey.swift in Sources */, - 30D75C3C1C57D1A300F4E62D /* KingCache.swift in Sources */, + 7BC24BB01D5C54E300B1C208 /* DoublyLinkedList.swift in Sources */, + 30D75C3C1C57D1A300F4E62D /* LRUCache.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -286,7 +292,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 30D75C441C57D2CA00F4E62D /* CacheIsKingTests.swift in Sources */, + 7BC24BAC1D5C432200B1C208 /* LRUCacheTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -295,7 +301,7 @@ /* Begin PBXTargetDependency section */ 30D75C481C57D2CA00F4E62D /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = 30D75C221C57CE1400F4E62D /* CacheIsKing-iOS */; + target = 30D75C221C57CE1400F4E62D /* CacheCreek-iOS */; targetProxy = 30D75C471C57D2CA00F4E62D /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ @@ -304,6 +310,8 @@ 30D75C1C1C57CD6100F4E62D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ENABLE_TESTABILITY = YES; + ONLY_ACTIVE_ARCH = YES; }; name = Debug; }; @@ -354,14 +362,14 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - INFOPLIST_FILE = "CacheIsKing-iOS/Info.plist"; + INFOPLIST_FILE = "CacheCreek-iOS/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "com.nuudles.CacheIsKing-iOS"; - PRODUCT_NAME = CacheIsKing; + PRODUCT_BUNDLE_IDENTIFIER = "com.nuudles.CacheCreek-iOS"; + PRODUCT_NAME = CacheCreek; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -406,13 +414,13 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - INFOPLIST_FILE = "CacheIsKing-iOS/Info.plist"; + INFOPLIST_FILE = "CacheCreek-iOS/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MTL_ENABLE_DEBUG_INFO = NO; - PRODUCT_BUNDLE_IDENTIFIER = "com.nuudles.CacheIsKing-iOS"; - PRODUCT_NAME = CacheIsKing; + PRODUCT_BUNDLE_IDENTIFIER = "com.nuudles.CacheCreek-iOS"; + PRODUCT_NAME = CacheCreek; SDKROOT = iphoneos; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = "1,2"; @@ -462,13 +470,13 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - INFOPLIST_FILE = "CacheIsKing-tvOS/Info.plist"; + INFOPLIST_FILE = "CacheCreek-tvOS/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "com.nuudles.CacheIsKing-tvOS"; - PRODUCT_NAME = CacheIsKing; + PRODUCT_BUNDLE_IDENTIFIER = "com.nuudles.CacheCreek-tvOS"; + PRODUCT_NAME = CacheCreek; SDKROOT = appletvos; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -513,12 +521,12 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - INFOPLIST_FILE = "CacheIsKing-tvOS/Info.plist"; + INFOPLIST_FILE = "CacheCreek-tvOS/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MTL_ENABLE_DEBUG_INFO = NO; - PRODUCT_BUNDLE_IDENTIFIER = "com.nuudles.CacheIsKing-tvOS"; - PRODUCT_NAME = CacheIsKing; + PRODUCT_BUNDLE_IDENTIFIER = "com.nuudles.CacheCreek-tvOS"; + PRODUCT_NAME = CacheCreek; SDKROOT = appletvos; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = 3; @@ -565,12 +573,12 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - INFOPLIST_FILE = CacheIsKingTests/Info.plist; + INFOPLIST_FILE = CacheCreekTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 9.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.nuudles.CacheIsKingTests; + PRODUCT_BUNDLE_IDENTIFIER = com.nuudles.CacheCreekTests; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -607,11 +615,11 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - INFOPLIST_FILE = CacheIsKingTests/Info.plist; + INFOPLIST_FILE = CacheCreekTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 9.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MTL_ENABLE_DEBUG_INFO = NO; - PRODUCT_BUNDLE_IDENTIFIER = com.nuudles.CacheIsKingTests; + PRODUCT_BUNDLE_IDENTIFIER = com.nuudles.CacheCreekTests; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; @@ -621,7 +629,7 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 30D75C1B1C57CD6100F4E62D /* Build configuration list for PBXProject "CacheIsKing" */ = { + 30D75C1B1C57CD6100F4E62D /* Build configuration list for PBXProject "CacheCreek" */ = { isa = XCConfigurationList; buildConfigurations = ( 30D75C1C1C57CD6100F4E62D /* Debug */, @@ -630,7 +638,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 30D75C291C57CE1400F4E62D /* Build configuration list for PBXNativeTarget "CacheIsKing-iOS" */ = { + 30D75C291C57CE1400F4E62D /* Build configuration list for PBXNativeTarget "CacheCreek-iOS" */ = { isa = XCConfigurationList; buildConfigurations = ( 30D75C2A1C57CE1400F4E62D /* Debug */, @@ -639,7 +647,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 30D75C391C57D17400F4E62D /* Build configuration list for PBXNativeTarget "CacheIsKing-tvOS" */ = { + 30D75C391C57D17400F4E62D /* Build configuration list for PBXNativeTarget "CacheCreek-tvOS" */ = { isa = XCConfigurationList; buildConfigurations = ( 30D75C3A1C57D17400F4E62D /* Debug */, @@ -648,7 +656,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 30D75C491C57D2CA00F4E62D /* Build configuration list for PBXNativeTarget "CacheIsKingTests" */ = { + 30D75C491C57D2CA00F4E62D /* Build configuration list for PBXNativeTarget "CacheCreekTests" */ = { isa = XCConfigurationList; buildConfigurations = ( 30D75C4A1C57D2CA00F4E62D /* Debug */, diff --git a/CacheIsKing.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/CacheCreek.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 58% rename from CacheIsKing.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to CacheCreek.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 5d805bd..719e274 100644 --- a/CacheIsKing.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/CacheCreek.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:/Users/sjs/Projects/CacheCreek/CacheCreek.xcodeproj"> diff --git a/CacheIsKing.xcodeproj/xcshareddata/xcschemes/CacheIsKing-iOS.xcscheme b/CacheCreek.xcodeproj/xcshareddata/xcschemes/CacheCreek-iOS.xcscheme similarity index 74% rename from CacheIsKing.xcodeproj/xcshareddata/xcschemes/CacheIsKing-iOS.xcscheme rename to CacheCreek.xcodeproj/xcshareddata/xcschemes/CacheCreek-iOS.xcscheme index 93561fe..4aaa406 100644 --- a/CacheIsKing.xcodeproj/xcshareddata/xcschemes/CacheIsKing-iOS.xcscheme +++ b/CacheCreek.xcodeproj/xcshareddata/xcschemes/CacheCreek-iOS.xcscheme @@ -1,6 +1,6 @@ + BuildableName = "CacheCreek.framework" + BlueprintName = "CacheCreek-iOS" + ReferencedContainer = "container:CacheCreek.xcodeproj"> @@ -26,17 +26,16 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES" - codeCoverageEnabled = "YES"> + shouldUseLaunchSchemeArgsEnv = "YES"> + BuildableName = "CacheCreekTests.xctest" + BlueprintName = "CacheCreekTests" + ReferencedContainer = "container:CacheCreek.xcodeproj"> @@ -44,9 +43,9 @@ + BuildableName = "CacheCreek.framework" + BlueprintName = "CacheCreek-iOS" + ReferencedContainer = "container:CacheCreek.xcodeproj"> @@ -66,9 +65,9 @@ + BuildableName = "CacheCreek.framework" + BlueprintName = "CacheCreek-iOS" + ReferencedContainer = "container:CacheCreek.xcodeproj"> @@ -84,9 +83,9 @@ + BuildableName = "CacheCreek.framework" + BlueprintName = "CacheCreek-iOS" + ReferencedContainer = "container:CacheCreek.xcodeproj"> diff --git a/CacheIsKing.xcodeproj/xcshareddata/xcschemes/CacheIsKing-tvOS.xcscheme b/CacheCreek.xcodeproj/xcshareddata/xcschemes/CacheCreek-tvOS.xcscheme similarity index 81% rename from CacheIsKing.xcodeproj/xcshareddata/xcschemes/CacheIsKing-tvOS.xcscheme rename to CacheCreek.xcodeproj/xcshareddata/xcschemes/CacheCreek-tvOS.xcscheme index 0cb35de..7b45372 100644 --- a/CacheIsKing.xcodeproj/xcshareddata/xcschemes/CacheIsKing-tvOS.xcscheme +++ b/CacheCreek.xcodeproj/xcshareddata/xcschemes/CacheCreek-tvOS.xcscheme @@ -1,6 +1,6 @@ + BuildableName = "CacheCreek.framework" + BlueprintName = "CacheCreek-tvOS" + ReferencedContainer = "container:CacheCreek.xcodeproj"> @@ -46,9 +46,9 @@ + BuildableName = "CacheCreek.framework" + BlueprintName = "CacheCreek-tvOS" + ReferencedContainer = "container:CacheCreek.xcodeproj"> @@ -64,9 +64,9 @@ + BuildableName = "CacheCreek.framework" + BlueprintName = "CacheCreek-tvOS" + ReferencedContainer = "container:CacheCreek.xcodeproj"> diff --git a/CacheIsKingTests/Info.plist b/CacheCreekTests/Info.plist similarity index 100% rename from CacheIsKingTests/Info.plist rename to CacheCreekTests/Info.plist diff --git a/CacheIsKingTests/CacheIsKingTests.swift b/CacheCreekTests/LRUCacheTests.swift similarity index 57% rename from CacheIsKingTests/CacheIsKingTests.swift rename to CacheCreekTests/LRUCacheTests.swift index af7e20b..dab0c57 100644 --- a/CacheIsKingTests/CacheIsKingTests.swift +++ b/CacheCreekTests/LRUCacheTests.swift @@ -1,39 +1,37 @@ // -// CacheIsKingTests.swift -// CacheIsKingTests +// CacheCreekTests.swift +// CacheCreekTests // // Created by Christopher Luu on 1/26/16. // // import XCTest -@testable import CacheIsKing +@testable import CacheCreek -class CacheIsKingTests: XCTestCase { - func testSettingAndGettingItems() { - let cache = KingCache() - cache.setItem(123, forKey: "123") +class CacheCreekTests: XCTestCase { + + typealias Node = DoublyLinkedListNode + + func testSettingAndGettingItems() { + let cache = LRUCache() + cache.set(item: 123, forKey: "123") - XCTAssert(cache.cacheDictionary.count == 1) XCTAssert(cache.count == 1) - XCTAssert(cache.itemForKey("123") == 123) - XCTAssert(cache.cacheDictionary[AnyKey("123")] as? Int == .Some(123)) + XCTAssert(cache.item(forKey: "123") == 123) XCTAssert(cache[123] == nil) cache[234] = "234" - XCTAssert(cache.cacheDictionary.count == 2) XCTAssert(cache.count == 2) - XCTAssert(cache.itemForKey(234) == "234") - XCTAssert(cache.cacheDictionary[AnyKey(234)] as? String == .Some("234")) + XCTAssert(cache.item(forKey: 234) == "234") // Test setting/getting an array let array = [1, 2, 3, 4, 5] cache[5] = array - XCTAssert(cache.cacheDictionary.count == 3) XCTAssert(cache.count == 3) - if let fetchedArray: [Int] = cache.itemForKey(5) { + if let fetchedArray: [Int] = cache.item(forKey: 5) { XCTAssert(fetchedArray == array) } else { @@ -43,7 +41,7 @@ class CacheIsKingTests: XCTestCase { let testStruct = TestStruct(name: "Testing", value: Int(arc4random_uniform(100000))) cache["TestingStruct"] = testStruct - guard let fetchedStruct: TestStruct = cache.itemForKey("TestingStruct") else { + guard let fetchedStruct: TestStruct = cache.item(forKey: "TestingStruct") else { XCTFail() return } @@ -52,39 +50,39 @@ class CacheIsKingTests: XCTestCase { } func testDifferentKindsOfKeys() { - let cache = KingCache() + let cache = LRUCache() let floatKey: Float = 123.456 - cache.setItem(123.456, forKey: floatKey) - XCTAssert(cache.itemForKey(floatKey) as Double? == .Some(123.456)) + cache.set(item: 123.456, forKey: floatKey) + XCTAssert(cache.item(forKey: floatKey) as Double? == .Some(123.456)) cache[floatKey] = 456.789 XCTAssert(cache.count == 1) XCTAssert(cache[floatKey] as? Double == .Some(456.789)) - cache.setItem("123.456", forKey: "123.456") + cache.set(item: "123.456", forKey: "123.456") XCTAssert(cache.count == 2) - XCTAssert(cache.itemForKey("123.456") as String? == .Some("123.456")) + XCTAssert(cache.item(forKey: "123.456") as String? == .Some("123.456")) let boolKey = true - cache.setItem(true, forKey: boolKey) + cache.set(item: true, forKey: boolKey) XCTAssert(cache.count == 3) - XCTAssert(cache.itemForKey(boolKey) as Bool? == .Some(true)) + XCTAssert(cache.item(forKey: boolKey) as Bool? == .Some(true)) - cache.removeItemForKey(boolKey) + cache.removeItem(forKey: boolKey) XCTAssert(cache.count == 2) - XCTAssert(cache.itemForKey(boolKey) as Bool? == .None) + XCTAssert(cache.item(forKey: boolKey) as Bool? == .None) } func testSettingAndGettingEnum() { - let cache = KingCache() + let cache = LRUCache() cache["ABC"] = TestEnum.ABC cache["DEF"] = TestEnum.DEF("BlahBlahBlah") cache["GHI"] = TestEnum.GHI(-500) - guard let abc: TestEnum = cache.itemForKey("ABC"), - def: TestEnum = cache.itemForKey("DEF"), - ghi: TestEnum = cache.itemForKey("GHI") + guard let abc: TestEnum = cache.item(forKey: "ABC"), + def: TestEnum = cache.item(forKey: "DEF"), + ghi: TestEnum = cache.item(forKey: "GHI") else { XCTFail() return @@ -99,7 +97,7 @@ class CacheIsKingTests: XCTestCase { } func testSubscripts() { - let cache = KingCache() + let cache = LRUCache() // Int subscript cache[123] = 123 @@ -131,15 +129,15 @@ class CacheIsKingTests: XCTestCase { } func testRemovingItems() { - let cache = KingCache() - cache.setItem(123, forKey: 123) - cache.setItem(234, forKey: 234) - cache.setItem(345, forKey: 345) - cache.setItem(456, forKey: 456) + let cache = LRUCache() + cache.set(item: 123, forKey: 123) + cache.set(item: 234, forKey: 234) + cache.set(item: 345, forKey: 345) + cache.set(item: 456, forKey: 456) XCTAssert(cache.count == 4) - cache.removeItemForKey(123) + cache.removeItem(forKey: 123) XCTAssert(cache.count == 3) XCTAssert(cache[123] == nil) @@ -153,10 +151,10 @@ class CacheIsKingTests: XCTestCase { XCTAssert(cache.count == 0) - cache.setItem(123, forKey: 123) - cache.setItem(234, forKey: 234) - cache.setItem(345, forKey: 345) - cache.setItem(456, forKey: 456) + cache.set(item: 123, forKey: 123) + cache.set(item: 234, forKey: 234) + cache.set(item: 345, forKey: 345) + cache.set(item: 456, forKey: 456) XCTAssert(cache.count == 4) @@ -164,10 +162,10 @@ class CacheIsKingTests: XCTestCase { XCTAssert(cache.count == 0) - cache.setItem(123, forKey: 123) - cache.setItem(234, forKey: 234) - cache.setItem(345, forKey: 345) - cache.setItem(456, forKey: 456) + cache.set(item: 123, forKey: 123) + cache.set(item: 234, forKey: 234) + cache.set(item: 345, forKey: 345) + cache.set(item: 456, forKey: 456) XCTAssert(cache.count == 4) @@ -175,10 +173,10 @@ class CacheIsKingTests: XCTestCase { XCTAssert(cache.count == 0) - cache.setItem(123, forKey: 123) - cache.setItem(234, forKey: 234) - cache.setItem(345, forKey: 345) - cache.setItem(456, forKey: 456) + cache.set(item: 123, forKey: 123) + cache.set(item: 234, forKey: 234) + cache.set(item: 345, forKey: 345) + cache.set(item: 456, forKey: 456) XCTAssert(cache.count == 4) @@ -187,17 +185,17 @@ class CacheIsKingTests: XCTestCase { XCTAssert(cache.count == 4) - cache.removeItemForKey(999) + cache.removeItem(forKey: 999) XCTAssert(cache.count == 4) } func testCountLimit() { - let cache = KingCache() - cache.setItem(123, forKey: 123) - cache.setItem(234, forKey: 234) - cache.setItem(345, forKey: 345) - cache.setItem(456, forKey: 456) + let cache = LRUCache() + cache.set(item: 123, forKey: 123) + cache.set(item: 234, forKey: 234) + cache.set(item: 345, forKey: 345) + cache.set(item: 456, forKey: 456) XCTAssert(cache.count == 4) @@ -209,10 +207,10 @@ class CacheIsKingTests: XCTestCase { XCTAssert(cache.count == 0) - cache.setItem(123, forKey: 123) - cache.setItem(234, forKey: 234) - cache.setItem(345, forKey: 345) - cache.setItem(456, forKey: 456) + cache.set(item: 123, forKey: 123) + cache.set(item: 234, forKey: 234) + cache.set(item: 345, forKey: 345) + cache.set(item: 456, forKey: 456) XCTAssert(cache.count == 3) @@ -221,10 +219,10 @@ class CacheIsKingTests: XCTestCase { cache.removeAllItems() - cache.setItem(123, forKey: 123) - cache.setItem(234, forKey: 234) - cache.setItem(345, forKey: 345) - cache.setItem(456, forKey: 456) + cache.set(item: 123, forKey: 123) + cache.set(item: 234, forKey: 234) + cache.set(item: 345, forKey: 345) + cache.set(item: 456, forKey: 456) cache.countLimit = 2 @@ -233,17 +231,17 @@ class CacheIsKingTests: XCTestCase { func testEmptyEviction() { // Make sure that an eviction on an empty dictionary doesn't crash - let cache = KingCache() - cache.evictItemsIfNeeded() + let cache = LRUCache() + cache.evictItems() } func testObjCObjects() { - let cache = KingCache() + let cache = LRUCache() let oldCache = NSCache() - cache.setItem(oldCache, forKey: "InceptionCache") + cache.set(item: oldCache, forKey: "InceptionCache") - guard let _: NSCache = cache.itemForKey("InceptionCache") else { + guard let _: NSCache = cache.item(forKey: "InceptionCache") else { XCTFail("Expected an NSCache object") return } diff --git a/CacheIsKing-iOS/CacheIsKing.h b/CacheIsKing-iOS/CacheIsKing.h deleted file mode 100644 index 558fd2e..0000000 --- a/CacheIsKing-iOS/CacheIsKing.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// CacheIsKing-iOS.h -// CacheIsKing-iOS -// -// Created by Christopher Luu on 1/26/16. -// -// - -#import - -//! Project version number for CacheIsKing-iOS. -FOUNDATION_EXPORT double CacheIsKingVersionNumber; - -//! Project version string for CacheIsKing-iOS. -FOUNDATION_EXPORT const unsigned char CacheIsKingVersionString[]; - -// In this header, you should import all the public headers of your framework using statements like #import - - diff --git a/CacheIsKing-tvOS/CacheIsKing.h b/CacheIsKing-tvOS/CacheIsKing.h deleted file mode 100644 index 3bd2407..0000000 --- a/CacheIsKing-tvOS/CacheIsKing.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// CacheIsKing-tvOS.h -// CacheIsKing-tvOS -// -// Created by Christopher Luu on 1/26/16. -// -// - -#import - -//! Project version number for CacheIsKing-tvOS. -FOUNDATION_EXPORT double CacheIsKingsVersionNumber; - -//! Project version string for CacheIsKing-tvOS. -FOUNDATION_EXPORT const unsigned char CacheIsKingVersionString[]; - -// In this header, you should import all the public headers of your framework using statements like #import - - diff --git a/CacheIsKing.podspec b/CacheIsKing.podspec deleted file mode 100644 index 6243c1c..0000000 --- a/CacheIsKing.podspec +++ /dev/null @@ -1,16 +0,0 @@ -Pod::Spec.new do |s| - s.name = 'CacheIsKing' - s.version = '0.0.2' - s.license = 'MIT' - s.summary = 'A simple cache that can hold anything, including Swift items' - s.homepage = 'https://github.com/nuudles/CacheIsKing' - s.authors = { 'Christopher Luu' => 'nuudles@gmail.com' } - s.source = { :git => 'https://github.com/nuudles/CacheIsKing.git', :tag => s.version } - - s.ios.deployment_target = '8.0' - s.tvos.deployment_target = '9.0' - - s.source_files = 'Source/*.swift' - - s.requires_arc = true -end diff --git a/README.md b/README.md index e13cc98..2395e6c 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,8 @@ -# CacheIsKing +# CacheCreek - - - +### Forked from [CacheIsKing]() -`CacheIsKing` is a simple cache that allows you to store any item, including objects, pure Swift structs, enums (with associated values), etc. Simply put, it's designed to act like an `NSCache` for everything, including Swift variables. +`CacheCreek` provides an an LRU cache that allows you to store any item, including objects, pure Swift structs, enums (with associated values), etc. Simply put, it's designed to act like an `NSCache` for everything, including Swift variables. ## Features @@ -25,16 +23,16 @@ [CocoaPods](http://cocoapods.org) is a dependency manager for Cocoa projects. -Because `CacheIsKing ` is written in Swift, you must use frameworks. +Because `CacheCreek` is written in Swift, you must use frameworks. -To integrate `CacheIsKing ` into your Xcode project using CocoaPods, specify it in your `Podfile`: +To integrate `CacheCreek` into your Xcode project using CocoaPods, specify it in your `Podfile`: ```ruby source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' use_frameworks! -pod 'CacheIsKing' +pod 'CacheCreek' ``` Then, run the following command: @@ -48,18 +46,18 @@ $ pod install Add this to your `Cartfile`: ``` -github "nuudles/CacheIsKing" +github "samsonjs/CacheCreek" ``` ## Usage -Simply use the `KingCache` class similar to how you'd use a `NSCache`. Using the `setItem` and `itemForKey` methods allow you to use type inference to get the values you want. +Simply use the `LRUCache` class similar to how you'd use a `NSCache`. Using the `setItem` and `itemForKey` methods allow you to use type inference to get the values you want. ```swift -let cache = KingCache() -cache.setItem(123, forKey: "123") +let cache = LRUCache() +cache.set(item: 123, forKey: "123") -if let item: Int = cache.itemForKey(456) { +if let item: Int = cache.item(forKey: 456) { doSomethingWithItem(item) } ``` @@ -67,7 +65,7 @@ if let item: Int = cache.itemForKey(456) { You can also use subscripts to set/get items from the cache. Unfortunately since Swift doesn't support subscript methods with generics yet, you'll have to cast your items as necessary. Also currently only `String`, `Int`, and `Float` keys are supported: ```swift -let cache = KingCache() +let cache = LRUCache() cache["123"] = 123 if let item = cache[456] as? Int { @@ -75,10 +73,10 @@ if let item = cache[456] as? Int { } ``` -The `KingCache` also has a `countLimit` property, which allows you to set the maximum number of items in the cache. It currently evicts randomly until the `countLimit` is met. +The `LRUCache` also has a `countLimit` property, which allows you to set the maximum number of items in the cache. ```swift -let cache = KingCache() +let cache = LRUCache() cache.countLimit = 2 cache[123] = 123 @@ -90,5 +88,9 @@ print("\(cache.count)") // shows a count of 2 ## TODO -- Refine eviction algorithm (currently evicts randomly) -- Update with better subscript support once Swift supports subscripts with generics \ No newline at end of file +- Update with better subscript support once Swift supports subscripts with generics + + +# License + +[MIT License](https://sjs.mit-license.org) diff --git a/Source/AnyKey.swift b/Source/AnyKey.swift index 3657825..fd08004 100644 --- a/Source/AnyKey.swift +++ b/Source/AnyKey.swift @@ -1,6 +1,6 @@ // // AnyKey.swift -// CacheIsKing +// CacheCreek // // Created by Christopher Luu on 1/26/16. // diff --git a/Source/DoublyLinkedList.swift b/Source/DoublyLinkedList.swift new file mode 100644 index 0000000..1bc157a --- /dev/null +++ b/Source/DoublyLinkedList.swift @@ -0,0 +1,100 @@ +// +// DoublyLinkedList.swift +// CacheCreek +// +// Created by Sami Samhuri on 2016-08-10. +// + +struct DoublyLinkedList { + + typealias Node = DoublyLinkedListNode + + private(set) var head: Node? + + private(set) var tail: Node? + + private(set) var count = 0 + + var isEmpty: Bool { + return head == nil + } + + mutating func prepend(key key: AnyKey, value: Any) -> Node { + let node = Node(key: key, value: value) + node.next = head + head?.prev = node + head = node + if tail == nil { + tail = node + } + count += 1 + return node + } + + mutating func append(key key: AnyKey, value: Any) -> Node { + let node = Node(key: key, value: value) + node.prev = tail + tail?.next = node + tail = node + if head == nil { + head = node + } + count += 1 + return node + } + + mutating func removeAll() { + head = nil + tail = nil + count = 0 + } + + mutating func removeLast() -> Node? { + if let node = tail { + remove(node: node) + return node + } + return nil + } + + mutating func remove(node node: Node) { + if let prev = node.prev { + prev.next = node.next + } + else { + head = node.next + } + if let next = node.next { + next.prev = node.prev + } + else { + tail = node.prev + } + node.next = nil + node.prev = nil + count -= 1 + } + + mutating func moveToHead(node node: Node) { + remove(node: node) + prepend(key: node.key, value: node.value) + } + +} + +class DoublyLinkedListNode { + + let key: AnyKey + + let value: Any + + var next: DoublyLinkedListNode? + + weak var prev: DoublyLinkedListNode? + + init(key: AnyKey, value: Any) { + self.key = key + self.value = value + } + +} diff --git a/Source/KingCache.swift b/Source/LRUCache.swift similarity index 59% rename from Source/KingCache.swift rename to Source/LRUCache.swift index 4e1a4cf..e029313 100644 --- a/Source/KingCache.swift +++ b/Source/LRUCache.swift @@ -1,41 +1,46 @@ // -// KingCache.swift -// CacheIsKing +// LRUCache.swift +// CacheCreek // // Created by Christopher Luu on 1/26/16. -// +// +// Modified to use LRU eviction by Sami Samhuri on 2016-08-10. // import Foundation -/// `KingCache` is a simple cache that can hold anything, including Swift structs, enums, and values. +/// `LRUCache` is an LRU cache that can hold anything, including Swift structs, enums, and values. /// It is designed to work similar to the `NSCache`, but with native Swift support. /// -public class KingCache { +public class LRUCache { // MARK: - Private variables /// An array of `NSNotificationCenter` observers that need to be removed upon deinitialization private var notificationObservers: [NSObjectProtocol] = [] - // MARK: - Internal variables - /// The dictionary that holds the cached values - var cacheDictionary: [AnyKey: Any] = [:] + /// The list of cached items. Most recently used item at head, least recently used item at tail. + private var items: DoublyLinkedList = DoublyLinkedList() + + /// Maps keys of cached items to nodes in the linked list. + private var keyToNodeMap: [AnyKey:DoublyLinkedListNode] = [:] // MARK: - Public variables - /// The number of items in the cache + /// The number of items in the cache. public var count: Int { - return cacheDictionary.count + return items.count } + /// The limit of the amount of items that can be held in the cache. This defaults to 0, which means there is no limit. - public var countLimit: UInt = 0 { + public var countLimit: Int = 0 { didSet { - evictItemsIfNeeded() + assert(countLimit >= 0) + evictItems() } } // MARK: - Initialization methods public init() { let removalBlock = { [unowned self] (_: NSNotification) in - self.cacheDictionary.removeAll() + self.removeAllItems() } var notificationObserver = NSNotificationCenter.defaultCenter() @@ -60,17 +65,13 @@ public class KingCache { // MARK: - Internal methods /// Evicts items if the `countLimit` has been reached. - /// This currently uses a random eviction policy, kicking out random items until the `countLimit` is satisfied. /// - func evictItemsIfNeeded() { - if countLimit > 0 && cacheDictionary.count > Int(countLimit) { - // TODO: Evict items with more rhyme or reason - var keys = cacheDictionary.keys.flatMap { $0 } - while cacheDictionary.count > Int(countLimit) { - let randomIndex = Int(arc4random_uniform(UInt32(keys.count))) - let key = keys.removeAtIndex(randomIndex) - cacheDictionary.removeValueForKey(key) - } + func evictItems() { + guard countLimit > 0 else { return } + while items.count > countLimit { + if let node = items.removeLast() { + keyToNodeMap[node.key] = nil + } } } @@ -80,9 +81,14 @@ public class KingCache { /// - parameter item: The item to be cached /// - parameter key: The key with which to cache the item /// - public func setItem(item: Any, forKey key: K) { - cacheDictionary[AnyKey(key)] = item - evictItemsIfNeeded() + public func set(item item: Any, forKey key: K) { + let key = AnyKey(key) + if let existingNode = keyToNodeMap[key] { + items.remove(node: existingNode) + } + let node = items.prepend(key: key, value: item) + keyToNodeMap[key] = node + evictItems() } /// Gets an item from the cache if it exists for a given `Hashable` key. @@ -93,8 +99,10 @@ public class KingCache { /// - parameter key: The key whose item should be fetched /// - returns: The item from the cache if it exists, or `nil` if an item could not be found /// - public func itemForKey(key: K) -> T? { - if let item = cacheDictionary[AnyKey(key)] as? T { + public func item(forKey key: K) -> T? { + let key = AnyKey(key) + if let node = keyToNodeMap[key], let item = node.value as? T { + items.moveToHead(node: node) return item } return nil @@ -104,14 +112,19 @@ public class KingCache { /// /// - parameter key: The key whose item should be removed /// - public func removeItemForKey(key: K) { - cacheDictionary[AnyKey(key)] = nil + public func removeItem(forKey key: K) { + let key = AnyKey(key) + if let node = keyToNodeMap[key] { + items.remove(node: node) + keyToNodeMap[key] = nil + } } /// Clears the entire cache. /// public func removeAllItems() { - cacheDictionary.removeAll() + items.removeAll() + keyToNodeMap.removeAll() } // MARK: - Subscript methods @@ -120,14 +133,14 @@ public class KingCache { /// public subscript(key: Int) -> Any? { get { - return itemForKey(key) + return item(forKey: key) } set { if let newValue = newValue { - setItem(newValue, forKey: key) + set(item: newValue, forKey: key) } else { - removeItemForKey(key) + removeItem(forKey: key) } } } @@ -136,14 +149,14 @@ public class KingCache { /// public subscript(key: Float) -> Any? { get { - return itemForKey(key) + return item(forKey: key) } set { if let newValue = newValue { - setItem(newValue, forKey: key) + set(item: newValue, forKey: key) } else { - removeItemForKey(key) + removeItem(forKey: key) } } } @@ -152,14 +165,14 @@ public class KingCache { /// public subscript(key: String) -> Any? { get { - return itemForKey(key) + return item(forKey: key) } set { if let newValue = newValue { - setItem(newValue, forKey: key) + set(item: newValue, forKey: key) } else { - removeItemForKey(key) + removeItem(forKey: key) } } }