I am using NSCache to implement caching in my app. I want to add expiration to it so it will obtain new data after some time. What are the options and what's the best approach?
Should I look at the timestamp when the cache is accessed and invalidate it then? Should the cache automatically invalidate itself by using a fixed interval timer?
Should the cache automatically invalidate itself by using a fixed interval timer?
This would be a bad solution, because you might add something seconds before the timer fires. The expiry should be based on the specific item's age. (It would, of course, be possible to conditionally invalidate items using a timer; see the comments on this answer.)
Here's an example. I thought about subclassing NSCache
, but decided it was simpler to use composition.
//
// ExpiringCache.h
//
// Created by Aaron Brager on 10/23/13.
#import <Foundation/Foundation.h>
@protocol ExpiringCacheItem <NSObject>
@property (nonatomic, strong) NSDate *expiringCacheItemDate;
@end
@interface ExpiringCache : NSObject
@property (nonatomic, strong) NSCache *cache;
@property (nonatomic, assign) NSTimeInterval expiryTimeInterval;
- (id)objectForKey:(id)key;
- (void)setObject:(NSObject <ExpiringCacheItem> *)obj forKey:(id)key;
@end
//
// ExpiringCache.m
//
// Created by Aaron Brager on 10/23/13.
#import "ExpiringCache.h"
@implementation ExpiringCache
- (instancetype) init {
self = [super init];
if (self) {
self.cache = [[NSCache alloc] init];
self.expiryTimeInterval = 3600; // default 1 hour
}
return self;
}
- (id)objectForKey:(id)key {
@try {
NSObject <ExpiringCacheItem> *object = [self.cache objectForKey:key];
if (object) {
NSTimeInterval timeSinceCache = fabs([object.expiringCacheItemDate timeIntervalSinceNow]);
if (timeSinceCache > self.expiryTimeInterval) {
[self.cache removeObjectForKey:key];
return nil;
}
}
return object;
}
@catch (NSException *exception) {
return nil;
}
}
- (void)setObject:(NSObject <ExpiringCacheItem> *)obj forKey:(id)key {
obj.expiringCacheItemDate = [NSDate date];
[self.cache setObject:obj forKey:key];
}
@end
setObject:forKey:cost:
since the NSCache documentation all but tells you not to use it.expiringCacheItemDate
. I thought about using respondsToSelector:
for this, but you could add an object that doesn't respond to that too, since NSCache takes id
and not NSObject
.#import "ExpiringCache.h"
@property (nonatomic, strong) ExpiringCache *accountsCache;
- (void) doSomething {
if (!self.accountsCache) {
self.accountsCache = [[ExpiringCache alloc] init];
self.accountsCache.expiryTimeInterval = 7200; // 2 hours
}
// add an object to the cache
[self.accountsCache setObject:newObj forKey:@"some key"];
// get an object
NSObject *cachedObj = [self.accountsCache objectForKey:@"some key"];
if (!cachedObj) {
// create a new one, this one is expired or we've never gotten it
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With