Objective C caching framework EGOCache in iOS App development

  • 2020-06-03 08:30:01
  • OfStack

EGOCache profile

EGOCache is a simple, thread-safe key value cache store. It has native support for NSString, UI/NSImage, and NSData, but can store anything that implements < NSCoding > . All cached items expire after the timeout, which by default, is one day.
EGOCache1 is a simple, thread-safe caching framework based on ES11en-ES12en. It has native support for NSString, UI/NSImage, and NSData, as well as for storing any class that implements the protocol.

EGOCache has only one class, EGOCache.h and EGOCache.m files. The usage is also easy to master, carefully study the method of EGOCache. h 1, you will soon get started.

EGOCache only provides disk caching, not memory caching. It also provides a way to clean up the cache:


- (void)clearCache;

EGOCache also provides a way to determine whether a cache exists:

- (BOOL)hasCacheForKey:(NSString* __nonnull)key;

Join the project directly through Cocoapods

Add the following line directly to Podfile of your project:


pod 'EGOCache'

Then execute:

$ pod update

EGOCache usage

Cache NSString with EGOCache

Storage:


NSString *saveString = @" Save me ";
[[EGOCache globalCache] setString:saveString forKey:[NSString stringWithFormat:@"EGOImageLoader-%lu", (unsigned long)[saveString hash]] withTimeoutInterval:24*60*60];

Read:

NSString *getSaveString = [[EGOCache globalCache] stringForKey:[NSString stringWithFormat:@"EGOImageLoader-%lu", (unsigned long)[@"SaveString" hash]]];

Does it feel similar to NSDictionary? Yes, we mentioned earlier that EGOCache is a caching framework based on ES61en-ES62en.

Cache UIImage with EGOCache

Storage:


 UIImage *saveImage = [UIImage imageNamed:@"iOSDevTip"];
[[EGOCache globalCache] setImage:saveImage forKey:[NSString stringWithFormat:@"EGOImageLoader-%lu", (unsigned long)[@"SaveImage" hash]] withTimeoutInterval:24*60*60];

Read:

UIImage *getSaveImage = [[EGOCache globalCache] imageForKey:[NSString stringWithFormat:@"EGOImageLoader-%lu", (unsigned long)[@"SaveImage" hash]]];

Cache NSData with EGOCache

Storage:


NSData *saveData = [NSData data];
[[EGOCache globalCache] setData:saveData forKey:[NSString stringWithFormat:@"EGOImageLoader-%lu", (unsigned long)[@"SaveData" hash]] withTimeoutInterval:24*60*60];

Read:

UIImage *getSaveData = [[EGOCache globalCache] dataForKey:[NSString stringWithFormat:@"EGOImageLoader-%lu", (unsigned long)[@"SaveData" hash]]];

How does EGOCache detect cache expiration
EGOCache can set the cache expiration time, which defaults to 1 day. Check 1 EGOCache source code, set the default time:


    [self setDefaultTimeoutInterval:86400];
    //86400 = 24 * 60 * 60 just 1 Day time.

Why does EGOCache provide a set cache expiration time? Or what's the benefit of setting a cache expiration date? I think the biggest benefit is the ability to clear the cache on a regular basis. You can set the cache time of an item, which makes it easy to manage the cache.

So here's the question:

How does EGOCache detect cache expiration time? When does the deletion of a cache entry occur after a time expiration is detected?

With these two questions in mind, let's continue our analysis.

How would you do that

Remember in the company, the boss often gives an example like this:

Comrade XXX, when he first came to our company, he always complained when he encountered problems. Never know how to think about the solution, only know to throw the problem to the leader. After working for half a year, I have grown up a lot. Now encounter a problem, not only to throw the problem out, but also to provide their own solutions...
I'm sure you've all heard of examples like this. Again, since we have posed these two problems, let's also think about 1. What if we do it?

If I were to write it, I would have a few preliminary ideas for how to do it:

Polling is carried out by the timer and detected once every 1 period of time. Write an while loop to detect. Each time a cache entry is read, determine if the cache time has expired. Returns the read cache entry without expiration; Otherwise, return nil. Of course, there are also 1 method, not 11 examples. When you think about it, it's easy to see the drawbacks of these methods.

Polling on a timer for a small cache is a waste of resources
Just like method 1.
Determine whether the app is expired each time it is read. If the 1 is not read, the app cache will become larger and larger, which is not desirable.
All of these methods have been ruled out. Is there any good method? Read on:

How is EGOCache implemented?

Take a closer look at EGOCache source, found in initWithCacheDirectory: method, each initialization EGOCache instance objects, traverses 1 times plist file all existing cache entry, take the time and the current time each cache entry, cache expiration time earlier than the current time, delete the corresponding cache file, and delete the corresponding key plist file record.

The specific implementation code is as follows:

Read cache item information


_cacheInfo = [[NSDictionary dictionaryWithContentsOfFile:cachePathForKey(_directory, @"EGOCache.plist")] mutableCopy];
if(!_cacheInfo) {
    _cacheInfo = [[NSMutableDictionary alloc] init];
}

Gets NSTimeInterval for the current time

NSTimeInterval now = [[NSDate date] timeIntervalSinceReferenceDate];

Declare removedKeys to hold key for expired cache entries

NSMutableArray* removedKeys = [[NSMutableArray alloc] init];

Iterate through the cache entry information and determine the cache time

for(NSString* key in _cacheInfo) {
    // Determines whether the cache entry expires earlier than the current time
    if([_cacheInfo[key] timeIntervalSinceReferenceDate] <= now) {
        // If the cache entry expires earlier than the current time, remove the cache entry
        [[NSFileManager defaultManager] removeItemAtPath:cachePathForKey(_directory, key) error:NULL];
        // Pair out of date cache entries key Save to removedKeys inside
        [removedKeys addObject:key];
    }
}

Delete expired cache entries for key

[_cacheInfo removeObjectsForKeys:removedKeys];

See these, not feel somebody else train of thought especially great, anyway, I think this author is not simple. Does that solve the problem in one step?

What else does EGOCache do?

Careful kids will notice that EGOCache is a single class, which means that the entire application cycle is only initialized once.


+ (instancetype)globalCache {
    static id instance;     static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[[self class] alloc] init];
    });
    return instance;
}

It is correct to determine whether a cache entry is out of date each time it is initialized. Consider 1 scenario:

The user opens app, and EGOCache is initialized and determines whether the cache entry is expired.
If there happens to be 1 cache entry that expires after EGOCache is initialized. We can still read the cache entry at this point. That's not right.
Further analysis of the EGOCache source code shows that when EGOCache reads a cache item, it first determines whether the cache item exists, then reads the cache item (note: read the cache item that was not expired when EGOCache was initialized, it does not say that it is not expired now), and finally determines whether the cache item read is expired compared to the current time.

Specific implementation is as follows:


- (BOOL)hasCacheForKey:(NSString*)key {
    // read EGOCache There are no expired cache entries when initialized
    NSDate* date = [self dateForKey:key];
    if(date == nil) return NO;
    // Determines whether the cached item being read is currently expired
    if([date timeIntervalSinceReferenceDate] < CFAbsoluteTimeGetCurrent()) return NO;
    return [[NSFileManager defaultManager] fileExistsAtPath:cachePathForKey(_directory, key)];
}
- (NSDate*)dateForKey:(NSString*)key {
    __block NSDate* date = nil;     dispatch_sync(_frozenCacheInfoQueue, ^{
        date = (self.frozenCacheInfo)[key];
    });     return date;
}

It is worth learning the idea of EGOCache to detect the expiration of cache time. It can be used for reference when encountering similar situations in the future.


Related articles: