一、关于yycache
开发 iOS 的老铁们对 yykit 应该非常熟悉了,不熟悉的可直接移步至:https://github.com/ibireme/YYCache,非常牛逼。
二、本文介绍
最近项目中遇到一个场景,需要指定某个 key 在存储时可指定有效期,若过期,则删除改 KEY 并 return nil,话不多说先上代码,文末讲大致的实现逻辑。
核心代码主要在YYCache.m 文件中
//
// YYCache.m
// YYCache <https://github.com/ibireme/YYCache>
//
// Created by ibireme on 15/2/13.
// Copyright (c) 2015 ibireme.
//
// This source code is licensed under the MIT-style license found in the
// LICENSE file in the root directory of this source tree.
//
#import "YYCache.h"
#import "YYMemoryCache.h"
#import "YYDiskCache.h"
//过期时间标识
static NSString * const EXPIRE_TIME = @"expire_time_";
@implementation YYCache
- (instancetype) init {
NSLog(@"Use \"initWithName\" or \"initWithPath\" to create YYCache instance.");
return [self initWithPath:@""];
}
- (instancetype)initWithName:(NSString *)name {
if (name.length == 0) return nil;
NSString *cacheFolder = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
NSString *path = [cacheFolder stringByAppendingPathComponent:name];
return [self initWithPath:path];
}
- (instancetype)initWithPath:(NSString *)path {
if (path.length == 0) return nil;
YYDiskCache *diskCache = [[YYDiskCache alloc] initWithPath:path];
if (!diskCache) return nil;
NSString *name = [path lastPathComponent];
YYMemoryCache *memoryCache = [YYMemoryCache new];
memoryCache.name = name;
self = [super init];
_name = name;
_diskCache = diskCache;
_memoryCache = memoryCache;
return self;
}
+ (instancetype)cacheWithAppId:(NSString *)appid {
return [[self alloc] initWithName:appid];
}
+ (instancetype)cacheWithPath:(NSString *)path {
return [[self alloc] initWithPath:path];
}
- (BOOL)containsObjectForKey:(NSString *)key {
//判断是否过期
BOOL b = [self checkKeyIsExpire:key];
if (b) {
return NO;
}
return [_memoryCache containsObjectForKey:key] || [_diskCache containsObjectForKey:key];
}
- (void)containsObjectForKey:(NSString *)key withBlock:(void (^)(NSString *key, BOOL contains))block {
if (!block) return;
//判断是否过期
BOOL b = [self checkKeyIsExpire:key];
if (b) {
[self removeObjectForKey:key];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
block(key, NO);
});
}else{
if ([_memoryCache containsObjectForKey:key]) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
block(key, YES);
});
} else {
[_diskCache containsObjectForKey:key withBlock:block];
}
}
}
- (id<NSCoding>)objectForKey:(NSString *)key {
//判断是否过期
BOOL b = [self checkKeyIsExpire:key];
if (b) {
[self removeObjectForKey:key];
return nil;
}
id<NSCoding> object = [_memoryCache objectForKey:key];
if (!object) {
object = [_diskCache objectForKey:key];
if (object) {
[_memoryCache setObject:object forKey:key];
}
}
return object;
}
- (void)objectForKey:(NSString *)key withBlock:(void (^)(NSString *key, id<NSCoding> object))block {
if (!block) return;
//判断是否过期
BOOL b = [self checkKeyIsExpire:key];
if (b) {
[self removeObjectForKey:key];
return ;
}
id<NSCoding> object = [_memoryCache objectForKey:key];
if (object) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
block(key, object);
});
} else {
[_diskCache objectForKey:key withBlock:^(NSString *key, id<NSCoding> object) {
if (object && ![_memoryCache objectForKey:key]) {
[_memoryCache setObject:object forKey:key];
}
block(key, object);
}];
}
}
- (void)setObject:(id<NSCoding>)object forKey:(NSString *)key {
[_memoryCache setObject:object forKey:key];
[_diskCache setObject:object forKey:key];
}
- (void)setObject:(id<NSCoding>)object forKey:(NSString *)key withBlock:(void (^)(void))block {
[_memoryCache setObject:object forKey:key];
[_diskCache setObject:object forKey:key withBlock:block];
}
- (void)setObject:(nullable id<NSCoding>)object forKey:(NSString *)key expiretime:(int)expiretime{
[_memoryCache setObject:object forKey:key];
[_diskCache setObject:object forKey:key];
//存储过期时间
NSString *timeKey = [self getTimeKey:key];
NSString *timeValue = [self getTimeValue:expiretime];
[_memoryCache setObject:timeValue forKey:timeKey];
[_diskCache setObject:timeValue forKey:timeKey];
}
- (void)setObject:(nullable id<NSCoding>)object forKey:(NSString *)key expiretime:(int)expiretime withBlock:(nullable void(^)(void))block{
[_memoryCache setObject:object forKey:key];
[_diskCache setObject:object forKey:key withBlock:block];
//存储过期时间
NSString *timeKey = [self getTimeKey:key];
NSString *timeValue = [self getTimeValue:expiretime];
[_memoryCache setObject:timeValue forKey:timeKey];
[_diskCache setObject:timeValue forKey:timeKey withBlock:nil];
}
- (void)removeObjectForKey:(NSString *)key {
[_memoryCache removeObjectForKey:key];
[_diskCache removeObjectForKey:key];
//删除时间 KEY
NSString *timeKey = [self getTimeKey:key];
[_memoryCache removeObjectForKey:timeKey];
[_diskCache removeObjectForKey:timeKey];
}
- (void)removeObjectForKey:(NSString *)key withBlock:(void (^)(NSString *key))block {
[_memoryCache removeObjectForKey:key];
[_diskCache removeObjectForKey:key withBlock:block];
//删除时间 KEY
NSString *timeKey = [self getTimeKey:key];
[_diskCache removeObjectForKey:timeKey withBlock:nil];
[_diskCache removeObjectForKey:timeKey];
}
- (void)removeAllObjects {
[_memoryCache removeAllObjects];
[_diskCache removeAllObjects];
}
- (void)removeAllObjectsWithBlock:(void(^)(void))block {
[_memoryCache removeAllObjects];
[_diskCache removeAllObjectsWithBlock:block];
}
- (void)removeAllObjectsWithProgressBlock:(void(^)(int removedCount, int totalCount))progress
endBlock:(void(^)(BOOL error))end {
[_memoryCache removeAllObjects];
[_diskCache removeAllObjectsWithProgressBlock:progress endBlock:end];
}
- (NSString *)description {
if (_name) return [NSString stringWithFormat:@"<%@: %p> (%@)", self.class, self, _name];
else return [NSString stringWithFormat:@"<%@: %p>", self.class, self];
}
/// 获取当前 秒的时间戳
-(NSString *)getNowTimeTimestamp{
NSDate* dat = [NSDate dateWithTimeIntervalSinceNow:0];
NSTimeInterval a=[dat timeIntervalSince1970];
NSString*timeString = [NSString stringWithFormat:@"%0.f", a];
return timeString;
}
///为 KEY 追加时间字符
-(NSString *)getTimeKey:(NSString *)key{
NSString *timeKey = [NSString stringWithFormat:@"%@%@",EXPIRE_TIME,key];
return timeKey;
}
/// 组装时间 value
/// @param expiretime 过期时间
-(NSString *)getTimeValue:(int)expiretime {
NSString *timestamp = [self getNowTimeTimestamp];
NSString *timeValue = [NSString stringWithFormat:@"%@-%d",timestamp,expiretime];
return timeValue;
}
根据 key 反查时间 KEY
-(BOOL)checkKeyIsExpire:(NSString *)key{
NSString * timeKey = [self getTimeKey:key];
id<NSCoding> timeValue = [_memoryCache objectForKey:timeKey];
if (!timeValue) {
timeValue = [_diskCache objectForKey:timeKey];
if (timeValue) {
[_memoryCache setObject:timeValue forKey:timeKey];
}
}
if(timeValue == nil){
return NO;
}
NSString *timeStringValue = [NSString stringWithFormat:@"%@",timeValue];
//分隔 “时间戳-过期时间”
@try {
NSArray *array = [timeStringValue componentsSeparatedByString:@"-"];
NSString *expireTimeStamp = array[0];
NSString *expiretime = array[1];
int longExpireTimeStamp = [expireTimeStamp intValue];
int intExpiretime = [expiretime intValue];
int longNewTimeStamp = [[self getNowTimeTimestamp] intValue];
int t = longNewTimeStamp - longExpireTimeStamp;
if (t >= intExpiretime) {
//删除过期的 时间 key
[self removeObjectForKey:key];
[self removeObjectForKey:timeKey];
return YES;
}
} @catch (NSException *exception) {
return NO;
NSLog(@"%@", exception.description);
}
return NO;
}
@end
YYCache.h 文件
//
// YYCache.h
// YYCache <https://github.com/ibireme/YYCache>
//
// Created by ibireme on 15/2/13.
// Copyright (c) 2015 ibireme.
//
// This source code is licensed under the MIT-style license found in the
// LICENSE file in the root directory of this source tree.
//
#import <Foundation/Foundation.h>
#if __has_include(<YYCache/YYCache.h>)
FOUNDATION_EXPORT double YYCacheVersionNumber;
FOUNDATION_EXPORT const unsigned char YYCacheVersionString[];
#import <YYCache/YYMemoryCache.h>
#import <YYCache/YYDiskCache.h>
#import <YYCache/YYKVStorage.h>
#elif __has_include(<YYWebImage/YYCache.h>)
#import <YYWebImage/YYMemoryCache.h>
#import <YYWebImage/YYDiskCache.h>
#import <YYWebImage/YYKVStorage.h>
#else
#import "YYMemoryCache.h"
#import "YYDiskCache.h"
#import "YYKVStorage.h"
#endif
NS_ASSUME_NONNULL_BEGIN
/**
`YYCache` is a thread safe key-value cache.
It use `YYMemoryCache` to store objects in a small and fast memory cache,
and use `YYDiskCache` to persisting objects to a large and slow disk cache.
See `YYMemoryCache` and `YYDiskCache` for more information.
*/
@interface YYCache : NSObject
/** The name of the cache, readonly. */
@property (copy, readonly) NSString *name;
/** The underlying memory cache. see `YYMemoryCache` for more information.*/
@property (strong, readonly) YYMemoryCache *memoryCache;
/** The underlying disk cache. see `YYDiskCache` for more information.*/
@property (strong, readonly) YYDiskCache *diskCache;
/**
Create a new instance with the specified name.
Multiple instances with the same name will make the cache unstable.
@param name The name of the cache. It will create a dictionary with the name in
the app's caches dictionary for disk cache. Once initialized you should not
read and write to this directory.
@result A new cache object, or nil if an error occurs.
*/
- (nullable instancetype)initWithName:(NSString *)name;
/**
Create a new instance with the specified path.
Multiple instances with the same name will make the cache unstable.
@param path Full path of a directory in which the cache will write data.
Once initialized you should not read and write to this directory.
@result A new cache object, or nil if an error occurs.
*/
- (nullable instancetype)initWithPath:(NSString *)path NS_DESIGNATED_INITIALIZER;
/**
根据a appid 初始化缓存实例
@param name The name of the cache. It will create a dictionary with the name in
the app's caches dictionary for disk cache. Once initialized you should not
read and write to this directory.
@result A new cache object, or nil if an error occurs.
*/
+ (nullable instancetype)cacheWithAppId:(NSString *)appid;
/**
Convenience Initializers
Create a new instance with the specified path.
Multiple instances with the same name will make the cache unstable.
@param path Full path of a directory in which the cache will write data.
Once initialized you should not read and write to this directory.
@result A new cache object, or nil if an error occurs.
*/
+ (nullable instancetype)cacheWithPath:(NSString *)path;
- (instancetype)init UNAVAILABLE_ATTRIBUTE;
+ (instancetype)new UNAVAILABLE_ATTRIBUTE;
#pragma mark - Access Methods
///=============================================================================
/// @name Access Methods
///=============================================================================
/**
Returns a boolean value that indicates whether a given key is in cache.
This method may blocks the calling thread until file read finished.
@param key A string identifying the value. If nil, just return NO.
@return Whether the key is in cache.
*/
- (BOOL)containsObjectForKey:(NSString *)key;
/**
Returns a boolean value with the block that indicates whether a given key is in cache.
This method returns immediately and invoke the passed block in background queue
when the operation finished.
@param key A string identifying the value. If nil, just return NO.
@param block A block which will be invoked in background queue when finished.
*/
- (void)containsObjectForKey:(NSString *)key withBlock:(nullable void(^)(NSString *key, BOOL contains))block;
/**
Returns the value associated with a given key.
This method may blocks the calling thread until file read finished.
@param key A string identifying the value. If nil, just return nil.
@return The value associated with key, or nil if no value is associated with key.
*/
- (nullable id<NSCoding>)objectForKey:(NSString *)key;
/**
Returns the value associated with a given key.
This method returns immediately and invoke the passed block in background queue
when the operation finished.
@param key A string identifying the value. If nil, just return nil.
@param block A block which will be invoked in background queue when finished.
*/
- (void)objectForKey:(NSString *)key withBlock:(nullable void(^)(NSString *key, id<NSCoding> object))block;
/**
Sets the value of the specified key in the cache.
This method may blocks the calling thread until file write finished.
@param object The object to be stored in the cache. If nil, it calls `removeObjectForKey:`.
@param key The key with which to associate the value. If nil, this method has no effect.
*/
- (void)setObject:(nullable id<NSCoding>)object forKey:(NSString *)key;
/**
Sets the value of the specified key in the cache.
This method returns immediately and invoke the passed block in background queue
when the operation finished.
@param object The object to be stored in the cache. If nil, it calls `removeObjectForKey:`.
@param block A block which will be invoked in background queue when finished.
*/
- (void)setObject:(nullable id<NSCoding>)object forKey:(NSString *)key withBlock:(nullable void(^)(void))block;
/**
Sets the value of the specified key in the cache.
This method may blocks the calling thread until file write finished.
@param object The object to be stored in the cache. If nil, it calls `removeObjectForKey:`.
@param key The key with which to associate the value. If nil, this method has no effect.
@param expiretime 过期时间
*/
- (void)setObject:(nullable id<NSCoding>)object forKey:(NSString *)key expiretime:(int)expiretime;
/**
Sets the value of the specified key in the cache.
This method returns immediately and invoke the passed block in background queue
when the operation finished.
@param object The object to be stored in the cache. If nil, it calls `removeObjectForKey:`.
@param block A block which will be invoked in background queue when finished.
@param expiretime 过期时间
*/
- (void)setObject:(nullable id<NSCoding>)object forKey:(NSString *)key expiretime:(int)expiretime withBlock:(nullable void(^)(void))block;
/**
Removes the value of the specified key in the cache.
This method may blocks the calling thread until file delete finished.
@param key The key identifying the value to be removed. If nil, this method has no effect.
*/
- (void)removeObjectForKey:(NSString *)key;
/**
Removes the value of the specified key in the cache.
This method returns immediately and invoke the passed block in background queue
when the operation finished.
@param key The key identifying the value to be removed. If nil, this method has no effect.
@param block A block which will be invoked in background queue when finished.
*/
- (void)removeObjectForKey:(NSString *)key withBlock:(nullable void(^)(NSString *key))block;
/**
Empties the cache.
This method may blocks the calling thread until file delete finished.
*/
- (void)removeAllObjects;
/**
Empties the cache.
This method returns immediately and invoke the passed block in background queue
when the operation finished.
@param block A block which will be invoked in background queue when finished.
*/
- (void)removeAllObjectsWithBlock:(void(^)(void))block;
/**
Empties the cache with block.
This method returns immediately and executes the clear operation with block in background.
@warning You should not send message to this instance in these blocks.
@param progress This block will be invoked during removing, pass nil to ignore.
@param end This block will be invoked at the end, pass nil to ignore.
*/
- (void)removeAllObjectsWithProgressBlock:(nullable void(^)(int removedCount, int totalCount))progress
endBlock:(nullable void(^)(BOOL error))end;
@end
NS_ASSUME_NONNULL_END
三、新增的 API 如下
/**
Sets the value of the specified key in the cache.
This method may blocks the calling thread until file write finished.
@param object The object to be stored in the cache. If nil, it calls `removeObjectForKey:`.
@param key The key with which to associate the value. If nil, this method has no effect.
@param expiretime 过期时间
*/
- (void)setObject:(nullable id<NSCoding>)object forKey:(NSString *)key expiretime:(int)expiretime;
/**
Sets the value of the specified key in the cache.
This method returns immediately and invoke the passed block in background queue
when the operation finished.
@param object The object to be stored in the cache. If nil, it calls `removeObjectForKey:`.
@param block A block which will be invoked in background queue when finished.
@param expiretime 过期时间
*/
- (void)setObject:(nullable id<NSCoding>)object forKey:(NSString *)key expiretime:(int)expiretime withBlock:(nullable void(^)(void))block;
/**
四、实现思路
1、在存储KEY 的时候根据 KEY 存储当前创建或者更新时间
2、在取 KEY 的时候,首先获取系统当前时间并和存储时间相减,如果大于设置的过期时间则自动删除改 KEY并返回 nil。