我正在使用NSCache在我的应用程序中实现缓存。我想为它添加过期,以便在一段时间后获取新数据。有哪些选择以及最佳方法是什么?
我应该查看访问缓存时的时间戳并使其无效吗?缓存是否应使用固定间隔计时器自动使自身无效?
答案 0 :(得分:8)
缓存是否应使用固定的自动使自身无效 间隔计时器?
这将是一个糟糕的解决方案,因为您可能会在计时器触发前添加几秒钟。到期时间应基于特定项目的年龄。 (当然,有可能有条件地使用计时器使项目无效;请参阅对此答案的评论。)
这是一个例子。我考虑过继承NSCache
,但是决定使用合成更简单。
//
// 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:
。expiringCacheItemDate
的缓存中。我考虑过使用respondsToSelector:
,但你可以添加一个对此不响应的对象,因为NSCache需要id
而不是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
}
}
答案 1 :(得分:1)
另一种解决方案是在设置对象时设置过期时间,并与对象的过期时间进行比较。
例如:
<强>用法强>
#import "PTCache.h"
NSInteger const PROFILE_CACHE_EXPIRE = 3600;
- (void) cacheSomething: (id) obj
forKey: (NSString*) key
{
[PTCache sharedInstance] setObject: obj
forKey: key
expire: PROFILE_CACHE_EXPIRE
];
}
<强>接口强>
#import <Foundation/Foundation.h>
@interface PTCache : NSCache
+ (PTCache *) sharedInstance;
- (void) setObject: (id) obj
forKey: (NSString *) key
expire: (NSInteger) seconds;
- (id) objectForKey: (NSString *) key;
@end
<强>实施强>
#import "PTCache.h"
@implementation PTCache
{
NSMutableArray * expireKeys;
}
+ (PTCache *) sharedInstance
{
static dispatch_once_t predicate = 0;
__strong static id sharedObject = nil;
dispatch_once(&predicate, ^{
sharedObject = [[self alloc] init];
});
return sharedObject;
}
- (id) init
{
if ( self = [super init])
{
expireKeys = [[NSMutableArray alloc] init];
}
return self;
}
/**
* Get Object
*
* @param NSString * key
* @return id obj
*
**/
- (id) objectForKey: (NSString *) key
{
id obj = [super objectForKey: key];
if( obj == nil)
{
return nil;
}
BOOL expired = [self hasExpired: key];
if( expired)
{
[super removeObjectForKey: key];
return nil;
}
return obj;
}
/**
* Set Object
*
* @param id obj
* @param NSString * key
* @param NSInteger seconds
*
*/
- (void) setObject: (id) obj
forKey: (NSString *) key
expire: (NSInteger) seconds
{
[super setObject: obj forKey: key];
[self updateExpireKey: key expire: seconds];
}
/**
* Update Expire Time for Key and Seconds to Expire
*
* @param NSString * key
* @param NSInteger seconds
*
**/
- (void) updateExpireKey: (NSString *) key
expire: (NSInteger) seconds
__block NSInteger index = -1;
[expireKeys enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop) {
if([obj[@"key"] isEqualToString: key])
{
index = idx;
*stop = YES;
return;
}
}];
NSNumber * expires = [NSNumber numberWithFloat: ([[NSDate date] timeIntervalSince1970] + seconds)];
if( index > -1)
{
[[expireKeys objectAtIndex: index] setObject: expires forKey: key];
}
else
{
NSMutableDictionary * element = [[NSMutableDictionary alloc] init];
[element setObject: key forKey: @"key"];
[element setObject: expires forKey: @"expire"];
[expireKeys addObject: element];
}
}
/**
* Has Expired for Key
*
**/
- (BOOL) hasExpired: (NSString *) key
{
NSNumber * expiredObj = [self getExpireTime: key];
NSDate * current = [NSDate date];
NSDate * expireDate = [NSDate dateWithTimeIntervalSince1970: [expiredObj doubleValue]];
return [current compare: expireDate] == NSOrderedDescending;
}
/**
* Get Expire Time
*
* @param NSString * key
* @param NSInteger
*
**/
- (NSNumber *) getExpireTime: (NSString *) key
{
__block NSNumber * expire = nil;
[expireKeys enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop) {
if([obj[@"key"] isEqualToString: key])
{
expire = obj[@"expire"];
*stop = YES;
return;
}
}];
return expire;
}
@end