Coder Social home page Coder Social logo

tmcache's Introduction

TMCache

⚠️ ⚠️ ⚠️ TMCACHE IS NO LONGER BEING ACTIVELY MAINTAINED. DETAILS HERE.

Fast parallel object cache for iOS and OS X.

Build Status Version Platform License

TMCache 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 (TMMemoryCache) and one on disk (TMDiskCache), all backed by GCD and safe to access from multiple threads simultaneously. On iOS, TMMemoryCache will clear itself when the app receives a memory warning or goes into the background. Objects stored in TMDiskCache remain until you trim the cache yourself, either manually or by setting a byte or age limit.

TMCache and TMDiskCache accept any object conforming to NSCoding. Put things in like this:

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

Get them back out like this:

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

TMMemoryCache allows for concurrent reads and serialized writes, while TMDiskCache serializes disk access across all instances in the app to increase performance and prevent file contention. TMCache 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 TMCache, 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:

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

Installation

Manually

Download the latest tag and drag the TMCache 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/tumblr/TMCache.git
git submodule update --init

CocoaPods

Add TMCache to your Podfile and run pod install.

Requirements

TMCache requires iOS 5.0 or OS X 10.7 and greater.

Contributing

Please see CONTRIBUTING.md for information on how to help out.

Contact

Bryan Irace

License

Copyright 2013 Tumblr, 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.

tmcache's People

Contributors

akkyie avatar digabriel avatar ejensen avatar flambert avatar hayashi311 avatar irace avatar jstn avatar mbbischoff avatar napoapo77 avatar vmartinelli avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

tmcache's Issues

Diskcache does not keep content ?

I'm using the cache to store images into cache (memory and disk)
My app needs to store 35 "small" images and after restarting the app, not all the images are coming from the disk cache...
Where could be the issue ? I have not added any configuration for time or size limit...

Too many dispatch threads

I'm loading some images asynchronously in a table view while scrolling.
This starts too many threads on some machines, only in some cases.

Dispatch Thread Soft Limit Reached:   64  (too many dispatch threads blocked in synchronous operations)
Dispatch Thread Hard Limit Reached:  512  (too many dispatch threads blocked in synchronous operations)

I cannot control the amount of concurrent threads, since everything is done using GCD, without heavily modifying TMCache.

Any ideas on how to resolve this?

objectForKey:block: fails to call completion block when asked to process "large" numbers of requests

I noticed this issue when trying to cache a number of images at the same time in my own project and was able to recreate it in a sample project by simply requesting lots of object lookups in quick succession:

for (int i=1; i<=100; i++)
{
    [[TMCache sharedCache] objectForKey:[NSString stringWithFormat:@"key %d",i]
                                  block:^(TMCache *cache, NSString *key, id object) {
                                      NSLog(@"completed %@",key);
                                  }];
}

I would expect the above to output:

completed key 1
completed key 2
completed key 3
…
completed key 100

but it generally drops out after 1 or 2, or sometimes doesn't respond at all.

By switching:

 _queue = dispatch_queue_create([queueName UTF8String], DISPATCH_QUEUE_CONCURRENT);

in TMCache.m:33 with:

_queue = dispatch_queue_create([queueName UTF8String], DISPATCH_QUEUE_PRIORITY_DEFAULT);

I was able to get the cache responding as expected but this may have other side effects that I am unaware of. Any thoughts?

"byteCount" get the wrong value after caching multiple times on the same key

A simple example:

self.myDiskCache = [[TMDiskCache alloc] initWithName:@"myDiskCache"];

UIImage *myImg = .....;  // 5MB

// call cache multiple times
dispatch_async(self.mySerialQueue, ^{
    for (int i=0; i<10; i++) {
            [self.myDiskCache setObject:myImg forKey:@"myKey" block:^(TMDiskCache *cache, NSString *key, id<NSCoding> object, NSURL *fileURL) {
                    NSLog(@"%f MB", cache.byteCount/pow(2., 20.));
            }];
    }
});

The expectation outputs is "5 MB" for all.

But the chain is 5 MB, 10 MB, ... 50 MB.

memory leak while reading from diskcache

I cache NSData in the diskcache like this:
[[[TMCache sharedCache] diskCache] setObject:cachedData forKey:cacheKey block:^(TMDiskCache *cache, NSString *key, id object, NSURL *fileURL) {}];

And read back like this:
tempData = (NSData *)[[[TMCache sharedCache] diskCache] objectForKey:cacheKey];

The thing is tempData is never freed, and Instruments shows that NSKeyedUnarchiver will call NSData's InitWithCoder which calls an unknown function to allocate memory.

I then tried to define a class like below:

// the .h file
@interface HPCachedData : NSObject
@Property NSString *key;
@Property NSData *value;
@EnD

// the .m file

  • (id)initWithCoder:(NSCoder *)decoder {
    self = [super init];
    if (!self) {
    return nil;
    }

    self.key = [decoder decodeObjectForKey:@"key"];
    self.value = [decoder decodeObjectForKey:@"value"];

    return self;
    }

  • (void)encodeWithCoder:(NSCoder *)encoder {
    [encoder encodeObject:self.key forKey:@"key"];
    [encoder encodeObject:self.value forKey:@"value"];
    }

The the memory is still not freed.

Another issue is in both cases, Instruments show 32 bytes leaks in NSConcreateMutableData.

What did I do wrong?
Thanks!

ageLimit is not an ageLimit

If I'm not mistaken, when I set an ageLimit, TMCache trims cache by age and repeat the operation every "ageLimit" using dispatch_after.

If I set an ageLimit of 10 sec on t=0, insert an object on t=5, that object keeps in cache for 15 sec.

I think this is not the expected behavior.

Problem becomes worse when ageLimit becomes larger.

Update changelog

Care to update the changelog for what's new on 1.2.2 and 1.2.1? ;-)

Build failure with -Wall due to cute #pragma

A project that builds using -Wall fails due to the unknown pragma:

#pragma HC SVNT DRACONES

used in TMCache.m. One must manually set -Wno-unknown-pragmas on the file in the Compile Sources section of the Build Phases.

Perhaps a note in the README or the necessity of the pragma could be considered?

If setObject:forKey:block is called for a key that already exists (i.e. update object for a key), the byte count is not changed to reflect the update.

In the 1.2.0 release:

- (void)setObject:(id <NSCoding>)object forKey:(NSString *)key block:(TMDiskCacheObjectBlock)block
NSNumber *diskFileSize = [values objectForKey:NSURLTotalFileAllocatedSizeKey];
if (diskFileSize) {
[strongSelf->_sizes setObject:diskFileSize forKey:key];
                strongSelf.byteCount = strongSelf->_byteCount + [diskFileSize unsignedIntegerValue]; // atomic
}

Should be changed to something like:

NSNumber *diskFileSize = [values objectForKey:NSURLTotalFileAllocatedSizeKey];

            if (diskFileSize) {

                NSNumber *oldEntry = [strongSelf->_sizes objectForKey:key];

                if (oldEntry && [oldEntry isKindOfClass:NSNumber.class]){
                    strongSelf.byteCount = strongSelf->_byteCount - [oldEntry unsignedIntegerValue];
                }

                [strongSelf->_sizes setObject:diskFileSize forKey:key];
                strongSelf.byteCount = strongSelf->_byteCount + [diskFileSize unsignedIntegerValue]; // atomic
            }

add more tests

There's a few already, but they're far from complete.

Completion block is not called

Hey,

I have a problem with the library.
Sometimes the completion block of objectForKey is never called.
It seems that the dispatch_async doesn't work in TMDiskCache.

question about cost limit

I have a question about cost limit in TMMemoryCache.
I set the cost limit, but when I called setObject from TMCache, the cost of object always was zero. Then the memory always was in increasing
Can anyone help me?

TMMemoryCahe Code

  • (void)setObject:(id)object forKey:(NSString *)key
    {
    [self setObject:object forKey:key withCost:0];
    }

Need my custom policy to clean disk cache..

Sometime, I need to clean the disk cache regarding specific rules (not time or size based)

By example, I would like to encode the cache key with attributes, iterate through the cache elements and remove some elements based on the key (encoded) attributes..

Do you think it is doable ?
Any other alternative to iterated the cache elements ?

I would be nice to call a generic "cleanCache" method interface where each developer could implement his own policy..

TMDiskCache fails silently when keys are too long

So if you try to store a file keyed by something like file://localhost/Users/peyton/Library/Application%20Support/iPhone%20Simulator/6.1/Applications/DA12CB50-EA95-479D-BC14-7A01A68914B3/Documents/CameraCache/9EE8E76D-41A9-4226-974A-38D298F8971A-48969-00000C866AB57336.gif, TMDiskCache will fail silently. After URL encoding, the filename is waaay too long.

Inside <sys/syslimits.h>, there's NAME_MAX, the maximum length of the last component of a path, and PATH_MAX the absolute limit on path length.

I leave it to you to decide what to do.

Not saving into disc cache, stores only in memory.

Hi, i did like this

[[TMCache sharedCache] setObject:Image
                                  forKey:@"ImageKey"
                                   block:^(TMCache *cache, NSString *key, UIImage *storedImage){}];

But, when i remove application from memory,
and re run application

[[TMCache sharedCache] objectForKey:@"ImageKey" ^(TMCache *cache, NSString *key, UIImage *storedImage) {
NSLog(@"%@",storedImage)
}];

returns Null.

I changed all properties: Max limit of bytes

Potential cache race can result in memory/disk cache inconsistency

When [TMCache objectForKey:block:] fails to find an object in memory, it falls back to the disk. If it finds the object on disk, it sets the object in memory. This makes sense.

However, if this is happening at the same time a new object is being set for the same key, there's a race to set the object in memory because [TMCache setObject:forKey:block:] sets the memory and disk caches in parallel.

Essentially, if calls to [TMCache objectForKey:block:] and [TMCache setObject:forKey:block:] are made in parallel, there's a chance the underlying calls land in this order:

  1. The get on the memory cache misses the object.
  2. The set on the memory cache sets the new object.
  3. The get on the disk cache finds the old object and it is re-set on the memory cache.
  4. The set on the disk cache sets the new object.

Until the memory cache is blown away, it will subsequently return the wrong value. This sample code reproduces the issue (it's a race, so obviously it won't repro every time):

TMCache* cache = [TMCache sharedCache];
[cache removeAllObjects];
[cache setObject:@"old" forKey:@"key"];
[cache.memoryCache removeAllObjects];

[cache objectForKey:@"key" block:^(TMCache* cache, NSString* key, id object) {
    NSLog(@"get %@", object);
}];

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
    [cache setObject:@"new" forKey:@"key" block:^(TMCache* cache, NSString* key, id object) {
        NSLog(@"set %@", object);
        NSLog(@"get2 %@", [cache objectForKey:@"key"]);
    }];
});

I expect that this issue manifests extremely rarely, but I figured it's worth pointing out anyway.

encodedString

2014-09-23 16 39 36

Please change charsToEscape with CFSTR(".:/"); on CFSTR(":/");
or make it changeable in the settings.

really need the file extension!)

MUSIC

i want sound from DAC

Why do the synchronous methods use dispatch_async?

I am writing a library to combine different cached responses so it will need to do multiple cache requests per method. So, I wanted to go to another thread just once, run all my cache blocks and then return. It's pretty important that this library is fast.

But, the synchronous methods in TMCache all seem to actually run in a queue. Is there a good reason for this? Why don't they just run on the thread they were called on? I don't want to wait for other cache requests to be completed, I want the data ASAP.
It seems like this is maybe to run everything on the same thread, but what in this library is not thread safe?

Thanks.

isPresentObjectForKey

A api to check if the object is present in the cache will be a great enhancement in this project.
I was thinking of adding a bloom filter to add this functionality. Need feedback if this is already present in some other way.

LRU or other policy to clean cache

Is it planned to add policies for cache (disk and memory) cleaning ?
It would be very useful especially for images memory caching..
I do not know any good library managing that :(

diskByteCount value not correct after removing object from cache

After taking a look in the code it seems a "+" needs to be removed by "-"

  • (BOOL)removeFileAndExecuteBlocksForKey:(NSString *)key
    {

before:
self.byteCount = _byteCount + [byteSize unsignedIntegerValue]; // atomic

should be:
self.byteCount = _byteCount - [byteSize unsignedIntegerValue]; // atomic

Crash inside TMDiskCache objectForKey:block

TMDiskCache.m line 331 (latest version)

Fatal Exception NSInvalidArgumentException
*** -[NSKeyedUnarchiver initForReadingWithData:]: incomprehensible archive (0x62, 0x70, 0x6c, 0x69, 0x73, 0x74, 0x30, 0x30)

The only thing I know about this crash is that object was an image downloaded from web. (though dozens of similar objects just work)

[TMCache sharedCache].diskCache.byteCount returns 0

I fill the cache with some images, I restart the app and I call the following code:

NSLog(@"%lu", (unsigned long)[TMCache sharedCache].diskCache.byteCount);

The logs is displaying 0 !!!

If I'm correct this method should return the number of bytes used on the disk used to save my images (35)...and it should not be 0...

[Question] MemoryWarning in 2.0

I'm confused with memory issues in 2.0.

If I don't implement this

[[NSNotificationCenter defaultCenter] addObserver:memoryCache
                                         selector:@selector(handleMemoryWarning)        
                                             name:UIApplicationDidReceiveMemoryWarningNotification     
                                           object:[UIApplication sharedApplication]];

[[NSNotificationCenter defaultCenter] addObserver:memoryCache
                                         selector:@selector(handleApplicationBackgrounding)        
                                             name:UIApplicationDidEnterBackgroundNotification     
                                           object:[UIApplication sharedApplication]];

TMCache will not release memory, although received Memory Warning Notification, right?

Question: This comment seems unwarranted

"@warning Access is protected for the duration of the block, but to maintain safe disk access do not access this fileURL after the block has ended. Do all work on the ."

I don't understand this warning. I presume this is because you want to enforce that it's done on the same queue as everything else, but why is this necessary? Isn't the file manager thread safe? Shouldn't I always be able to read files from different threads?

This is related to the next issue I'm about to post about the synchronous methods. I'll link it after I post.

Thanks for your help.

disscuss about thread safe

Forgive my pool english.
I know that elements stored in TMCache is thread safe. But, is there a way to ensure that objects returned by TMCache is thread safe, such as array or dictionary.
Just like this :
I get a array from TMCache in thread A, and then in thread B, I remove an object from the array while it was traversing the array in thread A. Then I get a crash.

I wish you can understand my express, my English is pool.

Cocoapods out-of-date

Current cocoapods version does not include the new initWithName: rootPath: method -- please update

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.