iOS基于YYCache 改造,支持缓存 KEY 过期

一、关于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。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值