Fast, non-deadlocking parallel object cache for iOS, tvOS and OS X

Overview

PINCache

CocoaPods Carthage compatible Build status

Fast, non-deadlocking parallel object cache for iOS and OS X.

PINCache is a fork of TMCache re-architected to fix issues with deadlocking caused by heavy use. It is a key/value store designed for persisting temporary objects that are expensive to reproduce, such as downloaded data or the results of slow processing. It is comprised of two self-similar stores, one in memory (PINMemoryCache) and one on disk (PINDiskCache), all backed by GCD and safe to access from multiple threads simultaneously. On iOS, PINMemoryCache will clear itself when the app receives a memory warning or goes into the background. Objects stored in PINDiskCache remain until you trim the cache yourself, either manually or by setting a byte or age limit.

PINCache and PINDiskCache accept any object conforming to NSCoding. Put things in like this:

Objective-C

UIImage *img = [[UIImage alloc] initWithData:data scale:[[UIScreen mainScreen] scale]];
[[PINCache sharedCache] setObject:img forKey:@"image" block:nil]; // returns immediately

Swift

let img = UIImage(data: data, scale:UIScreen.main.scale)
PINCache.shared().setObject(img, forKey: "img")

Get them back out like this:

Objective-C

[[PINCache sharedCache] objectForKeyAsync:@"image" block:^(PINCache *cache, NSString *key, id object) {
    UIImage *image = (UIImage *)object;
    NSLog(@"image scale: %f", image.scale);
}];

Swift

PINCache.shared().object(forKey: "image") { (cache, key, object) in
    if let image = object as? UIImage {
        print("image scale: %f", image.scale)
    }
}

Both PINMemoryCache and PINDiskCache use locks to protect reads and writes. PINCache coordinates them so that objects added to memory are available immediately to other threads while being written to disk safely in the background. Both caches are public properties of PINCache, so it's easy to manipulate one or the other separately if necessary.

Collections work too. Thanks to the magic of NSKeyedArchiver, objects repeated in a collection only occupy the space of one on disk:

Objective-C

NSArray *images = @[ image, image, image ];
[[PINCache sharedCache] setObject:images forKey:@"images"];
NSLog(@"3 for the price of 1: %d", [[[PINCache sharedCache] diskCache] byteCount]);

Swift

// In Swift, Array, String, and Dictionary are all value types.
let images = [image, image, image] as NSArray // Cast to NSArray
PINCache.shared.setObject(images, forKey: "images")
print("3 for the prices of 1: %d", PINCache.shared.diskCache.byteCount)

Installation

Manually

Download the latest tag and drag the PINCache folder into your Xcode project.

Install the docs by double clicking the .docset file under docs/, or view them online at cocoadocs.org

Git Submodule

git submodule add https://github.com/pinterest/PINCache.git
git submodule update --init

CocoaPods

Add PINCache to your Podfile and run pod install.

Carthage

Add the following line to your Cartfile and run carthage update --platform ios. Then follow this instruction of Carthage to embed the framework.

github "pinterest/PINCache"

Requirements

PINCache requires iOS 8.0, tvOS 9.0, watchOS 2.0 or macOS 10.8 and greater.

Contact

Garrett Moon

License

Copyright 2013 Tumblr, Inc. Copyright 2015 Pinterest, Inc.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Comments
  • PINCache doesn't build in Xcode 12 Beta 4

    PINCache doesn't build in Xcode 12 Beta 4

    It generates a lot of semantic issues:

    Incompatible block pointer types sending 'PINMemoryCacheObjectBlock _Nullable __strong' (aka 'void (^__strong)(PINMemoryCache * _Nonnull __strong, NSString * _Nonnull __strong, id _Nullable __strong)') to parameter of type 'PINCacheObjectBlock _Nonnull' (aka 'void (^)(id _Nonnull __strong, NSString * _Nonnull __strong, id _Nullable __strong)')

    opened by mycroftcanner 22
  • PINCache 2.2?

    PINCache 2.2?

    Hey there! I'm not sure if you all already had plans for other commits going into the next version bump for PINCache. If not, I'd be happy to make a PR with the CHANGELOG.md update and podspec version bump. We're anxious to integrate #63 into an internal pod we have, and unfortunately the CocoaPods podspec dsl for a pod's dependency doesn't allow us to specify a git sha.

    Are you all ready for PINCache 2.2?

    opened by benasher44 18
  • Add readBlock to objectForKey & writeBlock to setObject:forKey:

    Add readBlock to objectForKey & writeBlock to setObject:forKey:

    Generally this is a merge of https://github.com/tumblr/TMCache/pull/30/commits to PINCache. Read & Write blocks are quite useful when object isn't compatible with NSCoding or manual action needs to be performed

    opened by dimazen 18
  • Remove PIN_APP_EXTENSION requirement

    Remove PIN_APP_EXTENSION requirement

    This PR is might still be a bit rough around the edges, but everything now pod lib lints, and it also works in an extension without having to compile out the UIApplication background task bits. I could however get the __WATCHOS_UNAVAILABLE macro to work in the same way, so I just decided to screw that and compile TARGET_OS_WATCH incompatible bits out of the code.

    opened by benasher44 14
  • Turn off ARC exceptions

    Turn off ARC exceptions

    -fobjc-arc-exceptions is turned on for PINDiskCache, which, according to the docs, will cause that file to be significantly slower and larger in size. perhaps the exceptions could be replaced with assertion fails, or just allow the memory to leak

    opened by michaeleiselsc 13
  • added a way to set custom key encoder/decoder

    added a way to set custom key encoder/decoder

    Hi. First, thanks for an amazing project. We are using ASDK and PINRemoteImage extensively in our app. We started to use PINCache as separate cache and there are cases when we want our custom filenames. We have added block for custom custom encoding/decoding, so anyone can provide their implementation for filenames. This can be used also for file extensions. Maybe in the next version extension can be replaced with custom blocks. Please let us know if it’s okey to merge. Thanks.

    opened by hovox 10
  • Error in xcode12 compilation

    Error in xcode12 compilation

    When I upgraded to xcode12, the compiler reported 23 errors in the morning

    Incompatible block pointer types initializing '__strong PINCacheObjectBlock' (aka 'void (^__strong)(id<PINCaching> _Nonnull __strong, NSString * _Nonnull __strong, id _Nullable __strong)') with an expression of type 'PINDiskCacheObjectBlock _Nullable __strong' (aka 'void (^__strong)(PINDiskCache * _Nonnull __strong, NSString * _Nonnull __strong, id<NSCoding> _Nullable __strong)')

    opened by zhouxubin 9
  • Data emptying

    Data emptying

    Hello, Sir I use [PINCache sharedCache] in the APP to cache data. But when I came over and opened the APP for a while, I found that all my cached data had been cleared. [PINCache sharedCache] can cache data on disk and memory, why my data on disk is also cleared? Data emptying

    opened by bapidashu 9
  • Update memory cache header

    Update memory cache header

    Some nullable will make trouble. For example, objectForKey with no block parameter. In swift, it's trouble.

    For example, the following are ambiguous in Swift3. They have the same method signature.

      - (void)objectForKey:(NSString *)key block:(nullable PINMemoryCacheObjectBlock)block;
      - (nullable id)objectForKey:(NSString *)key
    
    opened by ToroLiu 8
  • Crash on -[PINDiskCache objectForKey:fileURL:]

    Crash on -[PINDiskCache objectForKey:fileURL:]

    Our crash reporting consistently logs crashes on -[PINDiskCache objectForKey:fileURL:]. This is one of our highest reported crashes. This is on version 2.1.

    Stack traces for the queue the crash occurs on look like the following:

    Thread : Crashed: com.pinterest.PINDiskCache Asynchronous Queue
    0  Foundation                     0x269dea08 -[NSKeyedUnarchiver initForReadingWithData:] + 439
    1  Foundation                     0x269de991 -[NSKeyedUnarchiver initForReadingWithData:] + 320
    2  Foundation                     0x26a37305 +[NSKeyedUnarchiver unarchiveObjectWithFile:] + 124
    3  Remind101                      0x37159f -[PINDiskCache objectForKey:fileURL:] (PINDiskCache.m:605)
    4  Remind101                      0x3707e1 __35-[PINDiskCache objectForKey:block:]_block_invoke (PINDiskCache.m:433)
    5  libdispatch.dylib              0x25dbdb5b _dispatch_call_block_and_release + 10
    6  libdispatch.dylib              0x25dc8365 _dispatch_async_redirect_invoke$VARIANT$mp + 1360
    7  libdispatch.dylib              0x25dcc921 _dispatch_root_queue_drain + 1560
    8  libdispatch.dylib              0x25dcc305 _dispatch_worker_thread3 + 96
    9  libsystem_pthread.dylib        0x25f7bb29 _pthread_wqthread + 1024
    10 libsystem_pthread.dylib        0x25f7b718 start_wqthread + 8
    

    All other threads are idle and the main thread is simply spinning the runloop when this happens:

    Thread : com.apple.main-thread
    0  libsystem_kernel.dylib         0x25ec4c24 mach_msg_trap + 20
    1  libsystem_kernel.dylib         0x25ec4a29 mach_msg + 40
    2  CoreFoundation                 0x26207355 __CFRunLoopServiceMachPort + 136
    3  CoreFoundation                 0x262056dd __CFRunLoopRun + 1036
    4  CoreFoundation                 0x26158bf9 CFRunLoopRunSpecific + 520
    5  CoreFoundation                 0x261589e5 CFRunLoopRunInMode + 108
    6  GraphicsServices               0x273a4ac9 GSEventRunModal + 160
    7  UIKit                          0x2a3e8ba1 UIApplicationMain + 144
    8  Remind101                      0x241703 main (main.m:16)
    9  libdispatch.dylib              0x25e07873 (Missing)
    

    There happen to be quite a large number of queues for PINDiskCache when this occurs though. Their threads look like so:

    Thread : com.pinterest.PINDiskCache Asynchronous Queue
    0  libsystem_kernel.dylib         0x25ec4c74 semaphore_wait_trap + 8
    1  libdispatch.dylib              0x25dcec83 _dispatch_semaphore_wait_slow + 190
    2  Remind101                      0x3714b1 -[PINDiskCache objectForKey:fileURL:] (PINDiskCache.m:599)
    3  Remind101                      0x3707e1 __35-[PINDiskCache objectForKey:block:]_block_invoke (PINDiskCache.m:433)
    4  libdispatch.dylib              0x25dbdb5b _dispatch_call_block_and_release + 10
    5  libdispatch.dylib              0x25dc8365 _dispatch_async_redirect_invoke$VARIANT$mp + 1360
    6  libdispatch.dylib              0x25dcc921 _dispatch_root_queue_drain + 1560
    7  libdispatch.dylib              0x25dcc305 _dispatch_worker_thread3 + 96
    8  libsystem_pthread.dylib        0x25f7bb29 _pthread_wqthread + 1024
    9  libsystem_pthread.dylib        0x25f7b718 start_wqthread + 8
    
    needs volunteer 
    opened by rex-remind101 8
  • pincache framework problem with Xcode12.3 version

    pincache framework problem with Xcode12.3 version

    Incompatible block pointer types sending 'void (^)(PINCache * _Nonnull __strong, NSString * _Nonnull __strong, id _Nullable __strong)' to parameter of type 'PINCacheObjectBlock _Nullable' (aka 'void (^)(id _Nonnull __strong, NSString * _Nonnull __strong, id _Nullable __strong)')

    close if no reply 
    opened by DhagatKinjal 7
  • Secure code by using NSSecureCoding instead of NSCoding

    Secure code by using NSSecureCoding instead of NSCoding

    Hello,

    Created this issue to address the vulnerability with using NSCoding. The NSCoding does not verify the type of object upon deserialization and therefore is vulnerable to object substitution attacks.

    To mitigate this vulnerability, Apple introduced the NSSecureCoding protocol along with the following secure methods of NSKeyedArchiver and NSKeyedUnarchiver, which are robust against this type of attack:

    // Secure NSKeyedUnarchiver methods
    - (instancetype)initForReadingFromData:(NSData *)data error:(NSError **)error;
    + (id)unarchivedObjectOfClass:(Class)cls fromData:(NSData *)data error:(NSError **)error;
    + (id)unarchivedObjectOfClasses:(NSSet<Class> *)classes fromData:(NSData *)data error:(NSError **)error;
    
    // Secure NSKeyedArchiver methods
    - (instancetype)initRequiringSecureCoding:(BOOL)requiresSecureCoding;
    + (NSData *)archivedDataWithRootObject:(id)object requiringSecureCoding:(BOOL)requiresSecureCoding error:(NSError **)error;
    

    Apple provides more information in the WWDC20 session, 'Securing Your App'.

    Various Places where NSCoding is being used: https://github.com/pinterest/PINCache/search?q=NSCoding

    opened by guraghav 0
  • Carthage vs SPM differences

    Carthage vs SPM differences

    So I'm currently migrating from Carthage to SPM. After doing so I started seeing crashes that looked to be related to PINCache, so i tested some more.

    I made two projects one using PINCache with SPM and one with Carthage thats the only difference. They basically just have this code:

    import UIKit
    import PINCache
    
    class ViewController: UIViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
            
            let cache = PINCache(name: "test")
            cache.setObjectAsync("test", forKey: "test", withAgeLimit: 666666)
        }
    }
    

    The one using Carthage seems to be working just fine however the one using SPM immediately throws this exception

    2021-10-14 18:35:46.328136+0200 CachingTest[11466:3492388] *** Assertion failure in -[PINDiskCache setObject:forKey:withAgeLimit:fileURL:], PINDiskCache.m:1184
    2021-10-14 18:35:46.328130+0200 CachingTest[11466:3492384] *** Assertion failure in -[PINMemoryCache setObject:forKey:withCost:ageLimit:], PINMemoryCache.m:510
    2021-10-14 18:35:46.357156+0200 CachingTest[11466:3492388] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'ttlCache must be set to YES if setting an object-level age limit.'
    *** First throw call stack:
    (0x1827fc05c 0x19ad16f54 0x1840bd99c 0x1028fb688 0x1028fb39c 0x1028f1838 0x10290544c 0x102906b14 0x102907ef0 0x102c04a20 0x102c06700 0x102c098a0 0x102c08af0 0x102c1a688 0x102c1b08c 0x1f2c31174 0x1f2c30f50)
    libc++abi: terminating with uncaught exception of type NSException
    dyld4 config: DYLD_LIBRARY_PATH=/usr/lib/system/introspection DYLD_INSERT_LIBRARIES=/Developer/usr/lib/libBacktraceRecording.dylib:/Developer/usr/lib/libMainThreadChecker.dylib:/Developer/Library/PrivateFrameworks/DTDDISupport.framework/libViewDebuggerSupport.dylib
    *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'ttlCache must be set to YES if setting an object-level age limit.'
    terminating with uncaught exception of type NSException
    

    What on earth am I doing wrong here? How can the functionality change based on if I use Carthage or SPM?

    Both SPM and Carthage dependencies have this version numbers:

    PINOperation at "1.2.1" PINCache at "3.0.3"

    opened by icedice 0
  • PINOperation dependency updated to 1.2.1 to support xcframework build

    PINOperation dependency updated to 1.2.1 to support xcframework build

    Hey, there is awesome Carthage feature for Xcode 12+ (https://github.com/Carthage/Carthage#migrating-a-project-from-framework-bundles-to-xcframeworks) with no need to apply a workaround

    PINOperation 1.2.1 fixes issues with xcframework build, that's important for PINCache and PINRemoteImage (I will create PINRemoteImage pull request with updated PINCache dependency at this repo)

    It might be more correct to merge this changes to 'xcframework' branch of PINCache and PINRemoteImage

    opened by nickaroot 0
  • Allow enumeration of PINCache

    Allow enumeration of PINCache

    Hello

    Firstly, thank you for PINCache, its been a breeze to set up and work with, so much appreciated!

    I'm wondering why the enumerateObjectsWithBlock and fam isn't part of the PinCaching protocol - it might be nice to be able to enumerate all objects in your cache(s) (without loading flushed to disk objects into ram unless you specifically request to do so).

    I get there's likely nuances im not thinking of, but being able to enumerate your entire cache is a use case that's very helpful on my end as unintuitive as it is.

    Obviously you can do that now with the memory and disk caches manually, but you run into cases the same object may be backed by both caches (hot and on disk).

    I imagine there's nuances to equality checking (ensuring the hashes for the disk cache object and memcache objects match esp if a new object has to be deserialized to check for equality - could be slow).

    Its early and I'm likely missing some subtle details - any chance for a global enumerateObjectsWithBlock which fast paths in ram and only loads the disk items that aren't hot? Could be nicer than having folks roll their own.

    Much appreciated and thanks again.

    enhancement 
    opened by vade 3
  • +[PINDiskCache moveItemAtURLToTrash:] (PINDiskCache.m:451) Crashed at  -[NSURL fileSystemRepresentation]

    +[PINDiskCache moveItemAtURLToTrash:] (PINDiskCache.m:451) Crashed at -[NSURL fileSystemRepresentation]

    The _sharedTrashURL generated with [[NSProcessInfo processInfo] globallyUniqueString]; contains %00%00%00%00 , lead to a crash.

    Device: iPhone Xr iOS 14.2 (18B92)

    *** -[NSURL fileSystemRepresentation]: conversion failed for file:///private/var/mobile/Containers/Data/Application/40AC63D8-3BD7-458B-BAC6-6396A78BD715/tmp/FE7F1C30-38EE-47ED-BB37-8FE73EA7873A-64416-000010D9F50E%00%00%00%00/7F3D3218-6F96-4724-9853-19D242240A2F-64416-000010D9F50F2CDB

    0 CoreFoundation | ___exceptionPreprocess + 220
    -- | --
    1 libobjc.A.dylib | objc_exception_throw + 60
    2 Foundation | -[NSURL(NSURL) fileSystemRepresentation] + 668
    3 Foundation | -[NSFilesystemItemMoveOperation main] + 124
    4 Foundation | ___NSOPERATION_IS_INVOKING_MAIN__ + 24
    5 Foundation | -[NSOperation start] + 808
    6 Foundation | -[NSFileManager moveItemAtURL:toURL:options:error:] + 312
    7 MyApp | +[PINDiskCache moveItemAtURLToTrash:] (PINDiskCache.m:451)
    8 MyApp | -[PINDiskCache removeFileAndExecuteBlocksForKey:] (PINDiskCache.m:688)
    9 MyApp | -[PINDiskCache trimDiskToSizeByDate:] (PINDiskCache.m:779)
    10 MyApp | __49-[PINDiskCache trimToSizeByDateAsync:completion:]_block_invoke (PINDiskCache.m:974)
    11 MyApp | __44-[PINOperationQueue scheduleNextOperations:]_block_invoke (PINOperationQueue.m:346)
    12 libdispatch.dylib | __dispatch_call_block_and_release + 32
    13 libdispatch.dylib | __dispatch_client_callout + 20
    14 libdispatch.dylib | __dispatch_lane_serial_drain + 580
    
    
    bug needs volunteer 
    opened by Jason-Stan-Lee 5
Releases(3.0.3)
Owner
Pinterest
Pinterest's Open Source Projects
Pinterest
Cache - Nothing but Cache.

Cache doesn't claim to be unique in this area, but it's not another monster library that gives you a god's power. It does nothing but caching, but it does it well. It offers a good public API with out-of-box implementations and great customization possibilities. Cache utilizes Codable in Swift 4 to perform serialization.

HyperRedink 2.7k Dec 28, 2022
Apple Asset Cache (Content Cache) Tools

AssetCacheTool A library and tool for interacting with both the local and remote asset caches. This is based on research I did a few years ago on the

Kenneth Endfinger 21 Jan 5, 2023
Cachyr A typesafe key-value data cache for iOS, macOS, tvOS and watchOS written in Swift.

Cachyr A typesafe key-value data cache for iOS, macOS, tvOS and watchOS written in Swift. There already exists plenty of cache solutions, so why creat

Norsk rikskringkasting (NRK) 124 Nov 24, 2022
Carlos - A simple but flexible cache, written in Swift for iOS 13+ and WatchOS 6 apps.

Carlos A simple but flexible cache, written in Swift for iOS 13+ and WatchOS 6 apps. Breaking Changes Carlos 1.0.0 has been migrated from PiedPiper de

National Media & Tech 628 Dec 3, 2022
Everyone tries to implement a cache at some point in their iOS app’s lifecycle, and this is ours.

Everyone tries to implement a cache at some point in their app’s lifecycle, and this is ours. This is a library that allows people to cache NSData wit

Spotify 1.2k Dec 28, 2022
CachyKit - A Caching Library is written in Swift that can cache JSON, Image, Zip or AnyObject with expiry date/TTYL and force refresh.

Nice threadsafe expirable cache management that can cache any object. Supports fetching from server, single object expire date, UIImageView loading etc.

Sadman Samee 122 Dec 28, 2022
Track is a thread safe cache write by Swift. Composed of DiskCache and MemoryCache which support LRU.

Track is a thread safe cache write by Swift. Composed of DiskCache and MemoryCache which support LRU. Features Thread safe: Implement by dispatch_sema

Cheer 268 Nov 21, 2022
🏈 Cache CocoaPods for faster rebuild and indexing Xcode project.

Motivation Working on a project with a huge amount of pods I had some troubles: - Slow and unnecessary indexing of pods targets, which implementation

Vyacheslav Khorkov 487 Jan 5, 2023
SwiftyCache is a dynamic and auto-managed cache written in Swift

SwiftyCache is a dynamic and auto-managed cache written in Swift. Unlike a simple cache system, it allows you to keep some data even in different executions. Guaranteeing persistence, when desired, without increasing the time required to save or recover data.

Antonio Guerra 3 Aug 28, 2022
A lightweight generic cache for iOS written in Swift with extra love for images.

Haneke is a lightweight generic cache for iOS and tvOS written in Swift 4. It's designed to be super-simple to use. Here's how you would initalize a J

Haneke 5.2k Dec 29, 2022
High performance cache framework for iOS.

YYCache High performance cache framework for iOS. (It's a component of YYKit) Performance You may download and compile the latest version of sqlite an

null 2.3k Dec 16, 2022
MemoryCache - type-safe, thread-safe memory cache class in Swift

MemoryCache is a memory cache class in swift. The MemoryCache class incorporates LRU policies, which ensure that a cache doesn’t

Yusuke Morishita 74 Nov 24, 2022
UITableView cell cache that cures scroll-lags on cell instantiating

UITableView + Cache https://github.com/Kilograpp/UITableView-Cache UITableView cell cache that cures scroll-lags on a cell instantiating. Introduction

null 73 Aug 6, 2021
💾 Simple memory & disk cache

Cache ?? Simple memory & disk cache Usage ??‍?? Default let cache = Cache<String>() try memory.save("MyValue", forKey: "MyKey") let cached = try cac

SeongHo Hong 2 Feb 28, 2022
Cache library for videos for React Native

@lowkey/react-native-cache Cache everything Installation npm install @lowkey/react-native-cache Usage import ReactNativeCache from "@lowkey/react-nati

Max Prokopenko 1 Oct 1, 2021
CachedAsyncImage is the simplest way to add cache to your AsyncImage.

CachedAsyncImage ??️ CachedAsyncImage is AsyncImage, but with cache capabilities. Usage CachedAsyncImage has the exact same API and behavior as AsyncI

Lorenzo Fiamingo 278 Jan 5, 2023
XCRemoteCache is a remote cache tool for Xcode projects.

XCRemoteCache is a remote cache tool for Xcode projects. It reuses target artifacts generated on a remote machine, served from a simple REST server. H

Spotify 737 Dec 27, 2022
A simple cache that can hold anything, including Swift items

CacheIsKing CacheIsKing is a simple cache that allows you to store any item, including objects, pure Swift structs, enums (with associated values), et

Christopher Luu 13 Jan 22, 2018
A simple but flexible cache

Carlos A simple but flexible cache, written in Swift for iOS 13+ and WatchOS 6 apps. Breaking Changes Carlos 1.0.0 has been migrated from PiedPiper de

National Media & Tech 628 Dec 3, 2022