seperate iOS project from main project

This commit is contained in:
pengfei.zhou
2019-12-04 13:29:26 +08:00
commit ca06b501d8
473 changed files with 40553 additions and 0 deletions

22
Example/Pods/YYCache/LICENSE generated Normal file
View File

@@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 ibireme <ibireme@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

171
Example/Pods/YYCache/README.md generated Executable file
View File

@@ -0,0 +1,171 @@
YYCache
==============
[![License MIT](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://raw.githubusercontent.com/ibireme/YYCache/master/LICENSE)&nbsp;
[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)&nbsp;
[![CocoaPods](http://img.shields.io/cocoapods/v/YYCache.svg?style=flat)](http://cocoapods.org/?q= YYCache)&nbsp;
[![CocoaPods](http://img.shields.io/cocoapods/p/YYCache.svg?style=flat)](http://cocoapods.org/?q= YYCache)&nbsp;
[![Support](https://img.shields.io/badge/support-iOS%206%2B%20-blue.svg?style=flat)](https://www.apple.com/nl/ios/)&nbsp;
[![Build Status](https://travis-ci.org/ibireme/YYCache.svg?branch=master)](https://travis-ci.org/ibireme/YYCache)
High performance cache framework for iOS.<br/>
(It's a component of [YYKit](https://github.com/ibireme/YYKit))
Performance
==============
![Memory cache benchmark result](https://raw.github.com/ibireme/YYCache/master/Benchmark/Result_memory.png
)
![Disk benchmark result](https://raw.github.com/ibireme/YYCache/master/Benchmark/Result_disk.png
)
You may [download](http://www.sqlite.org/download.html) and compile the latest version of sqlite and ignore the libsqlite3.dylib in iOS system to get higher performance.
See `Benchmark/CacheBenchmark.xcodeproj` for more benchmark case.
Features
==============
- **LRU**: Objects can be evicted with least-recently-used algorithm.
- **Limitation**: Cache limitation can be controlled with count, cost, age and free space.
- **Compatibility**: The API is similar to `NSCache`, all methods are thread-safe.
- **Memory Cache**
- **Release Control**: Objects can be released synchronously/asynchronously on main thread or background thread.
- **Automatically Clear**: It can be configured to automatically evict objects when receive memory warning or app enter background.
- **Disk Cache**
- **Customization**: It supports custom archive and unarchive method to store object which does not adopt NSCoding.
- **Storage Type Control**: It can automatically decide the storage type (sqlite / file) for each object to get
better performance.
Installation
==============
### CocoaPods
1. Add `pod 'YYCache'` to your Podfile.
2. Run `pod install` or `pod update`.
3. Import \<YYCache/YYCache.h\>.
### Carthage
1. Add `github "ibireme/YYCache"` to your Cartfile.
2. Run `carthage update --platform ios` and add the framework to your project.
3. Import \<YYCache/YYCache.h\>.
### Manually
1. Download all the files in the YYCache subdirectory.
2. Add the source files to your Xcode project.
3. Link with required frameworks:
* UIKit
* CoreFoundation
* QuartzCore
* sqlite3
4. Import `YYCache.h`.
Documentation
==============
Full API documentation is available on [CocoaDocs](http://cocoadocs.org/docsets/YYCache/).<br/>
You can also install documentation locally using [appledoc](https://github.com/tomaz/appledoc).
Requirements
==============
This library requires `iOS 6.0+` and `Xcode 7.0+`.
License
==============
YYCache is provided under the MIT license. See LICENSE file for details.
<br/><br/>
---
中文介绍
==============
高性能 iOS 缓存框架。<br/>
(该项目是 [YYKit](https://github.com/ibireme/YYKit) 组件之一)
性能
==============
iPhone 6 上,内存缓存每秒响应次数 (越高越好):
![Memory cache benchmark result](https://raw.github.com/ibireme/YYCache/master/Benchmark/Result_memory.png
)
iPhone 6 上,磁盘缓存每秒响应次数 (越高越好):
![Disk benchmark result](https://raw.github.com/ibireme/YYCache/master/Benchmark/Result_disk.png
)
推荐到 SQLite 官网[下载](http://www.sqlite.org/download.html)和编译最新的 SQLite以替换 iOS 自带的 libsqlite3.dylib以获得最高 1.53 倍的性能提升。
更多测试代码和用例见 `Benchmark/CacheBenchmark.xcodeproj`
特性
==============
- **LRU**: 缓存支持 LRU (least-recently-used) 淘汰算法。
- **缓存控制**: 支持多种缓存控制方法:总数量、总大小、存活时间、空闲空间。
- **兼容性**: API 基本和 `NSCache` 保持一致, 所有方法都是线程安全的。
- **内存缓存**
- **对象释放控制**: 对象的释放(release) 可以配置为同步或异步进行,可以配置在主线程或后台线程进行。
- **自动清空**: 当收到内存警告或 App 进入后台时,缓存可以配置为自动清空。
- **磁盘缓存**
- **可定制性**: 磁盘缓存支持自定义的归档解档方法,以支持那些没有实现 NSCoding 协议的对象。
- **存储类型控制**: 磁盘缓存支持对每个对象的存储类型 (SQLite/文件) 进行自动或手动控制,以获得更高的存取性能。
安装
==============
### CocoaPods
1. 在 Podfile 中添加 `pod 'YYCache'`
2. 执行 `pod install``pod update`
3. 导入 \<YYCache/YYCache.h\>。
### Carthage
1. 在 Cartfile 中添加 `github "ibireme/YYCache"`
2. 执行 `carthage update --platform ios` 并将生成的 framework 添加到你的工程。
3. 导入 \<YYCache/YYCache.h\>。
### 手动安装
1. 下载 YYCache 文件夹内的所有内容。
2. 将 YYCache 内的源文件添加(拖放)到你的工程。
3. 链接以下的 frameworks:
* UIKit
* CoreFoundation
* QuartzCore
* sqlite3
4. 导入 `YYCache.h`
文档
==============
你可以在 [CocoaDocs](http://cocoadocs.org/docsets/YYCache/) 查看在线 API 文档,也可以用 [appledoc](https://github.com/tomaz/appledoc) 本地生成文档。
系统要求
==============
该项目最低支持 `iOS 6.0``Xcode 7.0`
许可证
==============
YYCache 使用 MIT 许可证,详情见 LICENSE 文件。
相关链接
==============
[YYCache 设计思路与技术细节](http://blog.ibireme.com/2015/10/26/yycache/)

206
Example/Pods/YYCache/YYCache/YYCache.h generated Normal file
View File

@@ -0,0 +1,206 @@
//
// 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;
/**
Convenience Initializers
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)cacheWithName:(NSString *)name;
/**
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;
/**
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

139
Example/Pods/YYCache/YYCache/YYCache.m generated Normal file
View File

@@ -0,0 +1,139 @@
//
// 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"
@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)cacheWithName:(NSString *)name {
return [[self alloc] initWithName:name];
}
+ (instancetype)cacheWithPath:(NSString *)path {
return [[self alloc] initWithPath:path];
}
- (BOOL)containsObjectForKey:(NSString *)key {
return [_memoryCache containsObjectForKey:key] || [_diskCache containsObjectForKey:key];
}
- (void)containsObjectForKey:(NSString *)key withBlock:(void (^)(NSString *key, BOOL contains))block {
if (!block) return;
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 {
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;
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)removeObjectForKey:(NSString *)key {
[_memoryCache removeObjectForKey:key];
[_diskCache removeObjectForKey:key];
}
- (void)removeObjectForKey:(NSString *)key withBlock:(void (^)(NSString *key))block {
[_memoryCache removeObjectForKey:key];
[_diskCache removeObjectForKey:key withBlock:block];
}
- (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];
}
@end

412
Example/Pods/YYCache/YYCache/YYDiskCache.h generated Normal file
View File

@@ -0,0 +1,412 @@
//
// YYDiskCache.h
// YYCache <https://github.com/ibireme/YYCache>
//
// Created by ibireme on 15/2/11.
// 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>
NS_ASSUME_NONNULL_BEGIN
/**
YYDiskCache is a thread-safe cache that stores key-value pairs backed by SQLite
and file system (similar to NSURLCache's disk cache).
YYDiskCache has these features:
* It use LRU (least-recently-used) to remove objects.
* It can be controlled by cost, count, and age.
* It can be configured to automatically evict objects when there's no free disk space.
* It can automatically decide the storage type (sqlite/file) for each object to get
better performance.
You may compile the latest version of sqlite and ignore the libsqlite3.dylib in
iOS system to get 2x~4x speed up.
*/
@interface YYDiskCache : NSObject
#pragma mark - Attribute
///=============================================================================
/// @name Attribute
///=============================================================================
/** The name of the cache. Default is nil. */
@property (nullable, copy) NSString *name;
/** The path of the cache (read-only). */
@property (readonly) NSString *path;
/**
If the object's data size (in bytes) is larger than this value, then object will
be stored as a file, otherwise the object will be stored in sqlite.
0 means all objects will be stored as separated files, NSUIntegerMax means all
objects will be stored in sqlite.
The default value is 20480 (20KB).
*/
@property (readonly) NSUInteger inlineThreshold;
/**
If this block is not nil, then the block will be used to archive object instead
of NSKeyedArchiver. You can use this block to support the objects which do not
conform to the `NSCoding` protocol.
The default value is nil.
*/
@property (nullable, copy) NSData *(^customArchiveBlock)(id object);
/**
If this block is not nil, then the block will be used to unarchive object instead
of NSKeyedUnarchiver. You can use this block to support the objects which do not
conform to the `NSCoding` protocol.
The default value is nil.
*/
@property (nullable, copy) id (^customUnarchiveBlock)(NSData *data);
/**
When an object needs to be saved as a file, this block will be invoked to generate
a file name for a specified key. If the block is nil, the cache use md5(key) as
default file name.
The default value is nil.
*/
@property (nullable, copy) NSString *(^customFileNameBlock)(NSString *key);
#pragma mark - Limit
///=============================================================================
/// @name Limit
///=============================================================================
/**
The maximum number of objects the cache should hold.
@discussion The default value is NSUIntegerMax, which means no limit.
This is not a strict limit — if the cache goes over the limit, some objects in the
cache could be evicted later in background queue.
*/
@property NSUInteger countLimit;
/**
The maximum total cost that the cache can hold before it starts evicting objects.
@discussion The default value is NSUIntegerMax, which means no limit.
This is not a strict limit — if the cache goes over the limit, some objects in the
cache could be evicted later in background queue.
*/
@property NSUInteger costLimit;
/**
The maximum expiry time of objects in cache.
@discussion The default value is DBL_MAX, which means no limit.
This is not a strict limit — if an object goes over the limit, the objects could
be evicted later in background queue.
*/
@property NSTimeInterval ageLimit;
/**
The minimum free disk space (in bytes) which the cache should kept.
@discussion The default value is 0, which means no limit.
If the free disk space is lower than this value, the cache will remove objects
to free some disk space. This is not a strict limit—if the free disk space goes
over the limit, the objects could be evicted later in background queue.
*/
@property NSUInteger freeDiskSpaceLimit;
/**
The auto trim check time interval in seconds. Default is 60 (1 minute).
@discussion The cache holds an internal timer to check whether the cache reaches
its limits, and if the limit is reached, it begins to evict objects.
*/
@property NSTimeInterval autoTrimInterval;
/**
Set `YES` to enable error logs for debug.
*/
@property BOOL errorLogsEnabled;
#pragma mark - Initializer
///=============================================================================
/// @name Initializer
///=============================================================================
- (instancetype)init UNAVAILABLE_ATTRIBUTE;
+ (instancetype)new UNAVAILABLE_ATTRIBUTE;
/**
Create a new cache based on the specified path.
@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.
@return A new cache object, or nil if an error occurs.
@warning If the cache instance for the specified path already exists in memory,
this method will return it directly, instead of creating a new instance.
*/
- (nullable instancetype)initWithPath:(NSString *)path;
/**
The designated initializer.
@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.
@param threshold The data store inline threshold in bytes. If the object's data
size (in bytes) is larger than this value, then object will be stored as a
file, otherwise the object will be stored in sqlite. 0 means all objects will
be stored as separated files, NSUIntegerMax means all objects will be stored
in sqlite. If you don't know your object's size, 20480 is a good choice.
After first initialized you should not change this value of the specified path.
@return A new cache object, or nil if an error occurs.
@warning If the cache instance for the specified path already exists in memory,
this method will return it directly, instead of creating a new instance.
*/
- (nullable instancetype)initWithPath:(NSString *)path
inlineThreshold:(NSUInteger)threshold NS_DESIGNATED_INITIALIZER;
#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:(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:(void(^)(NSString *key, id<NSCoding> _Nullable 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:(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:(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;
/**
Returns the number of objects in this cache.
This method may blocks the calling thread until file read finished.
@return The total objects count.
*/
- (NSInteger)totalCount;
/**
Get the number of objects in this 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)totalCountWithBlock:(void(^)(NSInteger totalCount))block;
/**
Returns the total cost (in bytes) of objects in this cache.
This method may blocks the calling thread until file read finished.
@return The total objects cost in bytes.
*/
- (NSInteger)totalCost;
/**
Get the total cost (in bytes) of objects in this 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)totalCostWithBlock:(void(^)(NSInteger totalCost))block;
#pragma mark - Trim
///=============================================================================
/// @name Trim
///=============================================================================
/**
Removes objects from the cache use LRU, until the `totalCount` is below the specified value.
This method may blocks the calling thread until operation finished.
@param count The total count allowed to remain after the cache has been trimmed.
*/
- (void)trimToCount:(NSUInteger)count;
/**
Removes objects from the cache use LRU, until the `totalCount` is below the specified value.
This method returns immediately and invoke the passed block in background queue
when the operation finished.
@param count The total count allowed to remain after the cache has been trimmed.
@param block A block which will be invoked in background queue when finished.
*/
- (void)trimToCount:(NSUInteger)count withBlock:(void(^)(void))block;
/**
Removes objects from the cache use LRU, until the `totalCost` is below the specified value.
This method may blocks the calling thread until operation finished.
@param cost The total cost allowed to remain after the cache has been trimmed.
*/
- (void)trimToCost:(NSUInteger)cost;
/**
Removes objects from the cache use LRU, until the `totalCost` is below the specified value.
This method returns immediately and invoke the passed block in background queue
when the operation finished.
@param cost The total cost allowed to remain after the cache has been trimmed.
@param block A block which will be invoked in background queue when finished.
*/
- (void)trimToCost:(NSUInteger)cost withBlock:(void(^)(void))block;
/**
Removes objects from the cache use LRU, until all expiry objects removed by the specified value.
This method may blocks the calling thread until operation finished.
@param age The maximum age of the object.
*/
- (void)trimToAge:(NSTimeInterval)age;
/**
Removes objects from the cache use LRU, until all expiry objects removed by the specified value.
This method returns immediately and invoke the passed block in background queue
when the operation finished.
@param age The maximum age of the object.
@param block A block which will be invoked in background queue when finished.
*/
- (void)trimToAge:(NSTimeInterval)age withBlock:(void(^)(void))block;
#pragma mark - Extended Data
///=============================================================================
/// @name Extended Data
///=============================================================================
/**
Get extended data from an object.
@discussion See 'setExtendedData:toObject:' for more information.
@param object An object.
@return The extended data.
*/
+ (nullable NSData *)getExtendedDataFromObject:(id)object;
/**
Set extended data to an object.
@discussion You can set any extended data to an object before you save the object
to disk cache. The extended data will also be saved with this object. You can get
the extended data later with "getExtendedDataFromObject:".
@param extendedData The extended data (pass nil to remove).
@param object The object.
*/
+ (void)setExtendedData:(nullable NSData *)extendedData toObject:(id)object;
@end
NS_ASSUME_NONNULL_END

458
Example/Pods/YYCache/YYCache/YYDiskCache.m generated Normal file
View File

@@ -0,0 +1,458 @@
//
// YYDiskCache.m
// YYCache <https://github.com/ibireme/YYCache>
//
// Created by ibireme on 15/2/11.
// 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 "YYDiskCache.h"
#import "YYKVStorage.h"
#import <UIKit/UIKit.h>
#import <CommonCrypto/CommonCrypto.h>
#import <objc/runtime.h>
#import <time.h>
#define Lock() dispatch_semaphore_wait(self->_lock, DISPATCH_TIME_FOREVER)
#define Unlock() dispatch_semaphore_signal(self->_lock)
static const int extended_data_key;
/// Free disk space in bytes.
static int64_t _YYDiskSpaceFree() {
NSError *error = nil;
NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfFileSystemForPath:NSHomeDirectory() error:&error];
if (error) return -1;
int64_t space = [[attrs objectForKey:NSFileSystemFreeSize] longLongValue];
if (space < 0) space = -1;
return space;
}
/// String's md5 hash.
static NSString *_YYNSStringMD5(NSString *string) {
if (!string) return nil;
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
unsigned char result[CC_MD5_DIGEST_LENGTH];
CC_MD5(data.bytes, (CC_LONG)data.length, result);
return [NSString stringWithFormat:
@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
result[0], result[1], result[2], result[3],
result[4], result[5], result[6], result[7],
result[8], result[9], result[10], result[11],
result[12], result[13], result[14], result[15]
];
}
/// weak reference for all instances
static NSMapTable *_globalInstances;
static dispatch_semaphore_t _globalInstancesLock;
static void _YYDiskCacheInitGlobal() {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_globalInstancesLock = dispatch_semaphore_create(1);
_globalInstances = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory valueOptions:NSPointerFunctionsWeakMemory capacity:0];
});
}
static YYDiskCache *_YYDiskCacheGetGlobal(NSString *path) {
if (path.length == 0) return nil;
_YYDiskCacheInitGlobal();
dispatch_semaphore_wait(_globalInstancesLock, DISPATCH_TIME_FOREVER);
id cache = [_globalInstances objectForKey:path];
dispatch_semaphore_signal(_globalInstancesLock);
return cache;
}
static void _YYDiskCacheSetGlobal(YYDiskCache *cache) {
if (cache.path.length == 0) return;
_YYDiskCacheInitGlobal();
dispatch_semaphore_wait(_globalInstancesLock, DISPATCH_TIME_FOREVER);
[_globalInstances setObject:cache forKey:cache.path];
dispatch_semaphore_signal(_globalInstancesLock);
}
@implementation YYDiskCache {
YYKVStorage *_kv;
dispatch_semaphore_t _lock;
dispatch_queue_t _queue;
}
- (void)_trimRecursively {
__weak typeof(self) _self = self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_autoTrimInterval * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
__strong typeof(_self) self = _self;
if (!self) return;
[self _trimInBackground];
[self _trimRecursively];
});
}
- (void)_trimInBackground {
__weak typeof(self) _self = self;
dispatch_async(_queue, ^{
__strong typeof(_self) self = _self;
if (!self) return;
Lock();
[self _trimToCost:self.costLimit];
[self _trimToCount:self.countLimit];
[self _trimToAge:self.ageLimit];
[self _trimToFreeDiskSpace:self.freeDiskSpaceLimit];
Unlock();
});
}
- (void)_trimToCost:(NSUInteger)costLimit {
if (costLimit >= INT_MAX) return;
[_kv removeItemsToFitSize:(int)costLimit];
}
- (void)_trimToCount:(NSUInteger)countLimit {
if (countLimit >= INT_MAX) return;
[_kv removeItemsToFitCount:(int)countLimit];
}
- (void)_trimToAge:(NSTimeInterval)ageLimit {
if (ageLimit <= 0) {
[_kv removeAllItems];
return;
}
long timestamp = time(NULL);
if (timestamp <= ageLimit) return;
long age = timestamp - ageLimit;
if (age >= INT_MAX) return;
[_kv removeItemsEarlierThanTime:(int)age];
}
- (void)_trimToFreeDiskSpace:(NSUInteger)targetFreeDiskSpace {
if (targetFreeDiskSpace == 0) return;
int64_t totalBytes = [_kv getItemsSize];
if (totalBytes <= 0) return;
int64_t diskFreeBytes = _YYDiskSpaceFree();
if (diskFreeBytes < 0) return;
int64_t needTrimBytes = targetFreeDiskSpace - diskFreeBytes;
if (needTrimBytes <= 0) return;
int64_t costLimit = totalBytes - needTrimBytes;
if (costLimit < 0) costLimit = 0;
[self _trimToCost:(int)costLimit];
}
- (NSString *)_filenameForKey:(NSString *)key {
NSString *filename = nil;
if (_customFileNameBlock) filename = _customFileNameBlock(key);
if (!filename) filename = _YYNSStringMD5(key);
return filename;
}
- (void)_appWillBeTerminated {
Lock();
_kv = nil;
Unlock();
}
#pragma mark - public
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillTerminateNotification object:nil];
}
- (instancetype)init {
@throw [NSException exceptionWithName:@"YYDiskCache init error" reason:@"YYDiskCache must be initialized with a path. Use 'initWithPath:' or 'initWithPath:inlineThreshold:' instead." userInfo:nil];
return [self initWithPath:@"" inlineThreshold:0];
}
- (instancetype)initWithPath:(NSString *)path {
return [self initWithPath:path inlineThreshold:1024 * 20]; // 20KB
}
- (instancetype)initWithPath:(NSString *)path
inlineThreshold:(NSUInteger)threshold {
self = [super init];
if (!self) return nil;
YYDiskCache *globalCache = _YYDiskCacheGetGlobal(path);
if (globalCache) return globalCache;
YYKVStorageType type;
if (threshold == 0) {
type = YYKVStorageTypeFile;
} else if (threshold == NSUIntegerMax) {
type = YYKVStorageTypeSQLite;
} else {
type = YYKVStorageTypeMixed;
}
YYKVStorage *kv = [[YYKVStorage alloc] initWithPath:path type:type];
if (!kv) return nil;
_kv = kv;
_path = path;
_lock = dispatch_semaphore_create(1);
_queue = dispatch_queue_create("com.ibireme.cache.disk", DISPATCH_QUEUE_CONCURRENT);
_inlineThreshold = threshold;
_countLimit = NSUIntegerMax;
_costLimit = NSUIntegerMax;
_ageLimit = DBL_MAX;
_freeDiskSpaceLimit = 0;
_autoTrimInterval = 60;
[self _trimRecursively];
_YYDiskCacheSetGlobal(self);
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_appWillBeTerminated) name:UIApplicationWillTerminateNotification object:nil];
return self;
}
- (BOOL)containsObjectForKey:(NSString *)key {
if (!key) return NO;
Lock();
BOOL contains = [_kv itemExistsForKey:key];
Unlock();
return contains;
}
- (void)containsObjectForKey:(NSString *)key withBlock:(void(^)(NSString *key, BOOL contains))block {
if (!block) return;
__weak typeof(self) _self = self;
dispatch_async(_queue, ^{
__strong typeof(_self) self = _self;
BOOL contains = [self containsObjectForKey:key];
block(key, contains);
});
}
- (id<NSCoding>)objectForKey:(NSString *)key {
if (!key) return nil;
Lock();
YYKVStorageItem *item = [_kv getItemForKey:key];
Unlock();
if (!item.value) return nil;
id object = nil;
if (_customUnarchiveBlock) {
object = _customUnarchiveBlock(item.value);
} else {
@try {
object = [NSKeyedUnarchiver unarchiveObjectWithData:item.value];
}
@catch (NSException *exception) {
// nothing to do...
}
}
if (object && item.extendedData) {
[YYDiskCache setExtendedData:item.extendedData toObject:object];
}
return object;
}
- (void)objectForKey:(NSString *)key withBlock:(void(^)(NSString *key, id<NSCoding> object))block {
if (!block) return;
__weak typeof(self) _self = self;
dispatch_async(_queue, ^{
__strong typeof(_self) self = _self;
id<NSCoding> object = [self objectForKey:key];
block(key, object);
});
}
- (void)setObject:(id<NSCoding>)object forKey:(NSString *)key {
if (!key) return;
if (!object) {
[self removeObjectForKey:key];
return;
}
NSData *extendedData = [YYDiskCache getExtendedDataFromObject:object];
NSData *value = nil;
if (_customArchiveBlock) {
value = _customArchiveBlock(object);
} else {
@try {
value = [NSKeyedArchiver archivedDataWithRootObject:object];
}
@catch (NSException *exception) {
// nothing to do...
}
}
if (!value) return;
NSString *filename = nil;
if (_kv.type != YYKVStorageTypeSQLite) {
if (value.length > _inlineThreshold) {
filename = [self _filenameForKey:key];
}
}
Lock();
[_kv saveItemWithKey:key value:value filename:filename extendedData:extendedData];
Unlock();
}
- (void)setObject:(id<NSCoding>)object forKey:(NSString *)key withBlock:(void(^)(void))block {
__weak typeof(self) _self = self;
dispatch_async(_queue, ^{
__strong typeof(_self) self = _self;
[self setObject:object forKey:key];
if (block) block();
});
}
- (void)removeObjectForKey:(NSString *)key {
if (!key) return;
Lock();
[_kv removeItemForKey:key];
Unlock();
}
- (void)removeObjectForKey:(NSString *)key withBlock:(void(^)(NSString *key))block {
__weak typeof(self) _self = self;
dispatch_async(_queue, ^{
__strong typeof(_self) self = _self;
[self removeObjectForKey:key];
if (block) block(key);
});
}
- (void)removeAllObjects {
Lock();
[_kv removeAllItems];
Unlock();
}
- (void)removeAllObjectsWithBlock:(void(^)(void))block {
__weak typeof(self) _self = self;
dispatch_async(_queue, ^{
__strong typeof(_self) self = _self;
[self removeAllObjects];
if (block) block();
});
}
- (void)removeAllObjectsWithProgressBlock:(void(^)(int removedCount, int totalCount))progress
endBlock:(void(^)(BOOL error))end {
__weak typeof(self) _self = self;
dispatch_async(_queue, ^{
__strong typeof(_self) self = _self;
if (!self) {
if (end) end(YES);
return;
}
Lock();
[_kv removeAllItemsWithProgressBlock:progress endBlock:end];
Unlock();
});
}
- (NSInteger)totalCount {
Lock();
int count = [_kv getItemsCount];
Unlock();
return count;
}
- (void)totalCountWithBlock:(void(^)(NSInteger totalCount))block {
if (!block) return;
__weak typeof(self) _self = self;
dispatch_async(_queue, ^{
__strong typeof(_self) self = _self;
NSInteger totalCount = [self totalCount];
block(totalCount);
});
}
- (NSInteger)totalCost {
Lock();
int count = [_kv getItemsSize];
Unlock();
return count;
}
- (void)totalCostWithBlock:(void(^)(NSInteger totalCost))block {
if (!block) return;
__weak typeof(self) _self = self;
dispatch_async(_queue, ^{
__strong typeof(_self) self = _self;
NSInteger totalCost = [self totalCost];
block(totalCost);
});
}
- (void)trimToCount:(NSUInteger)count {
Lock();
[self _trimToCount:count];
Unlock();
}
- (void)trimToCount:(NSUInteger)count withBlock:(void(^)(void))block {
__weak typeof(self) _self = self;
dispatch_async(_queue, ^{
__strong typeof(_self) self = _self;
[self trimToCount:count];
if (block) block();
});
}
- (void)trimToCost:(NSUInteger)cost {
Lock();
[self _trimToCost:cost];
Unlock();
}
- (void)trimToCost:(NSUInteger)cost withBlock:(void(^)(void))block {
__weak typeof(self) _self = self;
dispatch_async(_queue, ^{
__strong typeof(_self) self = _self;
[self trimToCost:cost];
if (block) block();
});
}
- (void)trimToAge:(NSTimeInterval)age {
Lock();
[self _trimToAge:age];
Unlock();
}
- (void)trimToAge:(NSTimeInterval)age withBlock:(void(^)(void))block {
__weak typeof(self) _self = self;
dispatch_async(_queue, ^{
__strong typeof(_self) self = _self;
[self trimToAge:age];
if (block) block();
});
}
+ (NSData *)getExtendedDataFromObject:(id)object {
if (!object) return nil;
return (NSData *)objc_getAssociatedObject(object, &extended_data_key);
}
+ (void)setExtendedData:(NSData *)extendedData toObject:(id)object {
if (!object) return;
objc_setAssociatedObject(object, &extended_data_key, extendedData, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSString *)description {
if (_name) return [NSString stringWithFormat:@"<%@: %p> (%@:%@)", self.class, self, _name, _path];
else return [NSString stringWithFormat:@"<%@: %p> (%@)", self.class, self, _path];
}
- (BOOL)errorLogsEnabled {
Lock();
BOOL enabled = _kv.errorLogsEnabled;
Unlock();
return enabled;
}
- (void)setErrorLogsEnabled:(BOOL)errorLogsEnabled {
Lock();
_kv.errorLogsEnabled = errorLogsEnabled;
Unlock();
}
@end

325
Example/Pods/YYCache/YYCache/YYKVStorage.h generated Normal file
View File

@@ -0,0 +1,325 @@
//
// YYKVStorage.h
// YYCache <https://github.com/ibireme/YYCache>
//
// Created by ibireme on 15/4/22.
// 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>
NS_ASSUME_NONNULL_BEGIN
/**
YYKVStorageItem is used by `YYKVStorage` to store key-value pair and meta data.
Typically, you should not use this class directly.
*/
@interface YYKVStorageItem : NSObject
@property (nonatomic, strong) NSString *key; ///< key
@property (nonatomic, strong) NSData *value; ///< value
@property (nullable, nonatomic, strong) NSString *filename; ///< filename (nil if inline)
@property (nonatomic) int size; ///< value's size in bytes
@property (nonatomic) int modTime; ///< modification unix timestamp
@property (nonatomic) int accessTime; ///< last access unix timestamp
@property (nullable, nonatomic, strong) NSData *extendedData; ///< extended data (nil if no extended data)
@end
/**
Storage type, indicated where the `YYKVStorageItem.value` stored.
@discussion Typically, write data to sqlite is faster than extern file, but
reading performance is dependent on data size. In my test (on iPhone 6 64G),
read data from extern file is faster than from sqlite when the data is larger
than 20KB.
* If you want to store large number of small datas (such as contacts cache),
use YYKVStorageTypeSQLite to get better performance.
* If you want to store large files (such as image cache),
use YYKVStorageTypeFile to get better performance.
* You can use YYKVStorageTypeMixed and choice your storage type for each item.
See <http://www.sqlite.org/intern-v-extern-blob.html> for more information.
*/
typedef NS_ENUM(NSUInteger, YYKVStorageType) {
/// The `value` is stored as a file in file system.
YYKVStorageTypeFile = 0,
/// The `value` is stored in sqlite with blob type.
YYKVStorageTypeSQLite = 1,
/// The `value` is stored in file system or sqlite based on your choice.
YYKVStorageTypeMixed = 2,
};
/**
YYKVStorage is a key-value storage based on sqlite and file system.
Typically, you should not use this class directly.
@discussion The designated initializer for YYKVStorage is `initWithPath:type:`.
After initialized, a directory is created based on the `path` to hold key-value data.
Once initialized you should not read or write this directory without the instance.
You may compile the latest version of sqlite and ignore the libsqlite3.dylib in
iOS system to get 2x~4x speed up.
@warning The instance of this class is *NOT* thread safe, you need to make sure
that there's only one thread to access the instance at the same time. If you really
need to process large amounts of data in multi-thread, you should split the data
to multiple KVStorage instance (sharding).
*/
@interface YYKVStorage : NSObject
#pragma mark - Attribute
///=============================================================================
/// @name Attribute
///=============================================================================
@property (nonatomic, readonly) NSString *path; ///< The path of this storage.
@property (nonatomic, readonly) YYKVStorageType type; ///< The type of this storage.
@property (nonatomic) BOOL errorLogsEnabled; ///< Set `YES` to enable error logs for debug.
#pragma mark - Initializer
///=============================================================================
/// @name Initializer
///=============================================================================
- (instancetype)init UNAVAILABLE_ATTRIBUTE;
+ (instancetype)new UNAVAILABLE_ATTRIBUTE;
/**
The designated initializer.
@param path Full path of a directory in which the storage will write data. If
the directory is not exists, it will try to create one, otherwise it will
read the data in this directory.
@param type The storage type. After first initialized you should not change the
type of the specified path.
@return A new storage object, or nil if an error occurs.
@warning Multiple instances with the same path will make the storage unstable.
*/
- (nullable instancetype)initWithPath:(NSString *)path type:(YYKVStorageType)type NS_DESIGNATED_INITIALIZER;
#pragma mark - Save Items
///=============================================================================
/// @name Save Items
///=============================================================================
/**
Save an item or update the item with 'key' if it already exists.
@discussion This method will save the item.key, item.value, item.filename and
item.extendedData to disk or sqlite, other properties will be ignored. item.key
and item.value should not be empty (nil or zero length).
If the `type` is YYKVStorageTypeFile, then the item.filename should not be empty.
If the `type` is YYKVStorageTypeSQLite, then the item.filename will be ignored.
It the `type` is YYKVStorageTypeMixed, then the item.value will be saved to file
system if the item.filename is not empty, otherwise it will be saved to sqlite.
@param item An item.
@return Whether succeed.
*/
- (BOOL)saveItem:(YYKVStorageItem *)item;
/**
Save an item or update the item with 'key' if it already exists.
@discussion This method will save the key-value pair to sqlite. If the `type` is
YYKVStorageTypeFile, then this method will failed.
@param key The key, should not be empty (nil or zero length).
@param value The key, should not be empty (nil or zero length).
@return Whether succeed.
*/
- (BOOL)saveItemWithKey:(NSString *)key value:(NSData *)value;
/**
Save an item or update the item with 'key' if it already exists.
@discussion
If the `type` is YYKVStorageTypeFile, then the `filename` should not be empty.
If the `type` is YYKVStorageTypeSQLite, then the `filename` will be ignored.
It the `type` is YYKVStorageTypeMixed, then the `value` will be saved to file
system if the `filename` is not empty, otherwise it will be saved to sqlite.
@param key The key, should not be empty (nil or zero length).
@param value The key, should not be empty (nil or zero length).
@param filename The filename.
@param extendedData The extended data for this item (pass nil to ignore it).
@return Whether succeed.
*/
- (BOOL)saveItemWithKey:(NSString *)key
value:(NSData *)value
filename:(nullable NSString *)filename
extendedData:(nullable NSData *)extendedData;
#pragma mark - Remove Items
///=============================================================================
/// @name Remove Items
///=============================================================================
/**
Remove an item with 'key'.
@param key The item's key.
@return Whether succeed.
*/
- (BOOL)removeItemForKey:(NSString *)key;
/**
Remove items with an array of keys.
@param keys An array of specified keys.
@return Whether succeed.
*/
- (BOOL)removeItemForKeys:(NSArray<NSString *> *)keys;
/**
Remove all items which `value` is larger than a specified size.
@param size The maximum size in bytes.
@return Whether succeed.
*/
- (BOOL)removeItemsLargerThanSize:(int)size;
/**
Remove all items which last access time is earlier than a specified timestamp.
@param time The specified unix timestamp.
@return Whether succeed.
*/
- (BOOL)removeItemsEarlierThanTime:(int)time;
/**
Remove items to make the total size not larger than a specified size.
The least recently used (LRU) items will be removed first.
@param maxSize The specified size in bytes.
@return Whether succeed.
*/
- (BOOL)removeItemsToFitSize:(int)maxSize;
/**
Remove items to make the total count not larger than a specified count.
The least recently used (LRU) items will be removed first.
@param maxCount The specified item count.
@return Whether succeed.
*/
- (BOOL)removeItemsToFitCount:(int)maxCount;
/**
Remove all items in background queue.
@discussion This method will remove the files and sqlite database to a trash
folder, and then clear the folder in background queue. So this method is much
faster than `removeAllItemsWithProgressBlock:endBlock:`.
@return Whether succeed.
*/
- (BOOL)removeAllItems;
/**
Remove all items.
@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)removeAllItemsWithProgressBlock:(nullable void(^)(int removedCount, int totalCount))progress
endBlock:(nullable void(^)(BOOL error))end;
#pragma mark - Get Items
///=============================================================================
/// @name Get Items
///=============================================================================
/**
Get item with a specified key.
@param key A specified key.
@return Item for the key, or nil if not exists / error occurs.
*/
- (nullable YYKVStorageItem *)getItemForKey:(NSString *)key;
/**
Get item information with a specified key.
The `value` in this item will be ignored.
@param key A specified key.
@return Item information for the key, or nil if not exists / error occurs.
*/
- (nullable YYKVStorageItem *)getItemInfoForKey:(NSString *)key;
/**
Get item value with a specified key.
@param key A specified key.
@return Item's value, or nil if not exists / error occurs.
*/
- (nullable NSData *)getItemValueForKey:(NSString *)key;
/**
Get items with an array of keys.
@param keys An array of specified keys.
@return An array of `YYKVStorageItem`, or nil if not exists / error occurs.
*/
- (nullable NSArray<YYKVStorageItem *> *)getItemForKeys:(NSArray<NSString *> *)keys;
/**
Get item infomartions with an array of keys.
The `value` in items will be ignored.
@param keys An array of specified keys.
@return An array of `YYKVStorageItem`, or nil if not exists / error occurs.
*/
- (nullable NSArray<YYKVStorageItem *> *)getItemInfoForKeys:(NSArray<NSString *> *)keys;
/**
Get items value with an array of keys.
@param keys An array of specified keys.
@return A dictionary which key is 'key' and value is 'value', or nil if not
exists / error occurs.
*/
- (nullable NSDictionary<NSString *, NSData *> *)getItemValueForKeys:(NSArray<NSString *> *)keys;
#pragma mark - Get Storage Status
///=============================================================================
/// @name Get Storage Status
///=============================================================================
/**
Whether an item exists for a specified key.
@param key A specified key.
@return `YES` if there's an item exists for the key, `NO` if not exists or an error occurs.
*/
- (BOOL)itemExistsForKey:(NSString *)key;
/**
Get total item count.
@return Total item count, -1 when an error occurs.
*/
- (int)getItemsCount;
/**
Get item value's total size in bytes.
@return Total size in bytes, -1 when an error occurs.
*/
- (int)getItemsSize;
@end
NS_ASSUME_NONNULL_END

1069
Example/Pods/YYCache/YYCache/YYKVStorage.m generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,213 @@
//
// YYMemoryCache.h
// YYCache <https://github.com/ibireme/YYCache>
//
// Created by ibireme on 15/2/7.
// 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>
NS_ASSUME_NONNULL_BEGIN
/**
YYMemoryCache is a fast in-memory cache that stores key-value pairs.
In contrast to NSDictionary, keys are retained and not copied.
The API and performance is similar to `NSCache`, all methods are thread-safe.
YYMemoryCache objects differ from NSCache in a few ways:
* It uses LRU (least-recently-used) to remove objects; NSCache's eviction method
is non-deterministic.
* It can be controlled by cost, count and age; NSCache's limits are imprecise.
* It can be configured to automatically evict objects when receive memory
warning or app enter background.
The time of `Access Methods` in YYMemoryCache is typically in constant time (O(1)).
*/
@interface YYMemoryCache : NSObject
#pragma mark - Attribute
///=============================================================================
/// @name Attribute
///=============================================================================
/** The name of the cache. Default is nil. */
@property (nullable, copy) NSString *name;
/** The number of objects in the cache (read-only) */
@property (readonly) NSUInteger totalCount;
/** The total cost of objects in the cache (read-only). */
@property (readonly) NSUInteger totalCost;
#pragma mark - Limit
///=============================================================================
/// @name Limit
///=============================================================================
/**
The maximum number of objects the cache should hold.
@discussion The default value is NSUIntegerMax, which means no limit.
This is not a strict limit—if the cache goes over the limit, some objects in the
cache could be evicted later in backgound thread.
*/
@property NSUInteger countLimit;
/**
The maximum total cost that the cache can hold before it starts evicting objects.
@discussion The default value is NSUIntegerMax, which means no limit.
This is not a strict limit—if the cache goes over the limit, some objects in the
cache could be evicted later in backgound thread.
*/
@property NSUInteger costLimit;
/**
The maximum expiry time of objects in cache.
@discussion The default value is DBL_MAX, which means no limit.
This is not a strict limit—if an object goes over the limit, the object could
be evicted later in backgound thread.
*/
@property NSTimeInterval ageLimit;
/**
The auto trim check time interval in seconds. Default is 5.0.
@discussion The cache holds an internal timer to check whether the cache reaches
its limits, and if the limit is reached, it begins to evict objects.
*/
@property NSTimeInterval autoTrimInterval;
/**
If `YES`, the cache will remove all objects when the app receives a memory warning.
The default value is `YES`.
*/
@property BOOL shouldRemoveAllObjectsOnMemoryWarning;
/**
If `YES`, The cache will remove all objects when the app enter background.
The default value is `YES`.
*/
@property BOOL shouldRemoveAllObjectsWhenEnteringBackground;
/**
A block to be executed when the app receives a memory warning.
The default value is nil.
*/
@property (nullable, copy) void(^didReceiveMemoryWarningBlock)(YYMemoryCache *cache);
/**
A block to be executed when the app enter background.
The default value is nil.
*/
@property (nullable, copy) void(^didEnterBackgroundBlock)(YYMemoryCache *cache);
/**
If `YES`, the key-value pair will be released on main thread, otherwise on
background thread. Default is NO.
@discussion You may set this value to `YES` if the key-value object contains
the instance which should be released in main thread (such as UIView/CALayer).
*/
@property BOOL releaseOnMainThread;
/**
If `YES`, the key-value pair will be released asynchronously to avoid blocking
the access methods, otherwise it will be released in the access method
(such as removeObjectForKey:). Default is YES.
*/
@property BOOL releaseAsynchronously;
#pragma mark - Access Methods
///=============================================================================
/// @name Access Methods
///=============================================================================
/**
Returns a Boolean value that indicates whether a given key is in cache.
@param key An object identifying the value. If nil, just return `NO`.
@return Whether the key is in cache.
*/
- (BOOL)containsObjectForKey:(id)key;
/**
Returns the value associated with a given key.
@param key An object 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)objectForKey:(id)key;
/**
Sets the value of the specified key in the cache (0 cost).
@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.
@discussion Unlike an NSMutableDictionary object, a cache does not copy the key
objects that are put into it.
*/
- (void)setObject:(nullable id)object forKey:(id)key;
/**
Sets the value of the specified key in the cache, and associates the key-value
pair with the specified cost.
@param object The object to store 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 cost The cost with which to associate the key-value pair.
@discussion Unlike an NSMutableDictionary object, a cache does not copy the key
objects that are put into it.
*/
- (void)setObject:(nullable id)object forKey:(id)key withCost:(NSUInteger)cost;
/**
Removes the value of the specified key in the cache.
@param key The key identifying the value to be removed. If nil, this method has no effect.
*/
- (void)removeObjectForKey:(id)key;
/**
Empties the cache immediately.
*/
- (void)removeAllObjects;
#pragma mark - Trim
///=============================================================================
/// @name Trim
///=============================================================================
/**
Removes objects from the cache with LRU, until the `totalCount` is below or equal to
the specified value.
@param count The total count allowed to remain after the cache has been trimmed.
*/
- (void)trimToCount:(NSUInteger)count;
/**
Removes objects from the cache with LRU, until the `totalCost` is or equal to
the specified value.
@param cost The total cost allowed to remain after the cache has been trimmed.
*/
- (void)trimToCost:(NSUInteger)cost;
/**
Removes objects from the cache with LRU, until all expiry objects removed by the
specified value.
@param age The maximum age (in seconds) of objects.
*/
- (void)trimToAge:(NSTimeInterval)age;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,505 @@
//
// YYMemoryCache.m
// YYCache <https://github.com/ibireme/YYCache>
//
// Created by ibireme on 15/2/7.
// 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 "YYMemoryCache.h"
#import <UIKit/UIKit.h>
#import <CoreFoundation/CoreFoundation.h>
#import <QuartzCore/QuartzCore.h>
#import <pthread.h>
static inline dispatch_queue_t YYMemoryCacheGetReleaseQueue() {
return dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
}
/**
A node in linked map.
Typically, you should not use this class directly.
*/
@interface _YYLinkedMapNode : NSObject {
@package
__unsafe_unretained _YYLinkedMapNode *_prev; // retained by dic
__unsafe_unretained _YYLinkedMapNode *_next; // retained by dic
id _key;
id _value;
NSUInteger _cost;
NSTimeInterval _time;
}
@end
@implementation _YYLinkedMapNode
@end
/**
A linked map used by YYMemoryCache.
It's not thread-safe and does not validate the parameters.
Typically, you should not use this class directly.
*/
@interface _YYLinkedMap : NSObject {
@package
CFMutableDictionaryRef _dic; // do not set object directly
NSUInteger _totalCost;
NSUInteger _totalCount;
_YYLinkedMapNode *_head; // MRU, do not change it directly
_YYLinkedMapNode *_tail; // LRU, do not change it directly
BOOL _releaseOnMainThread;
BOOL _releaseAsynchronously;
}
/// Insert a node at head and update the total cost.
/// Node and node.key should not be nil.
- (void)insertNodeAtHead:(_YYLinkedMapNode *)node;
/// Bring a inner node to header.
/// Node should already inside the dic.
- (void)bringNodeToHead:(_YYLinkedMapNode *)node;
/// Remove a inner node and update the total cost.
/// Node should already inside the dic.
- (void)removeNode:(_YYLinkedMapNode *)node;
/// Remove tail node if exist.
- (_YYLinkedMapNode *)removeTailNode;
/// Remove all node in background queue.
- (void)removeAll;
@end
@implementation _YYLinkedMap
- (instancetype)init {
self = [super init];
_dic = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
_releaseOnMainThread = NO;
_releaseAsynchronously = YES;
return self;
}
- (void)dealloc {
CFRelease(_dic);
}
- (void)insertNodeAtHead:(_YYLinkedMapNode *)node {
CFDictionarySetValue(_dic, (__bridge const void *)(node->_key), (__bridge const void *)(node));
_totalCost += node->_cost;
_totalCount++;
if (_head) {
node->_next = _head;
_head->_prev = node;
_head = node;
} else {
_head = _tail = node;
}
}
- (void)bringNodeToHead:(_YYLinkedMapNode *)node {
if (_head == node) return;
if (_tail == node) {
_tail = node->_prev;
_tail->_next = nil;
} else {
node->_next->_prev = node->_prev;
node->_prev->_next = node->_next;
}
node->_next = _head;
node->_prev = nil;
_head->_prev = node;
_head = node;
}
- (void)removeNode:(_YYLinkedMapNode *)node {
CFDictionaryRemoveValue(_dic, (__bridge const void *)(node->_key));
_totalCost -= node->_cost;
_totalCount--;
if (node->_next) node->_next->_prev = node->_prev;
if (node->_prev) node->_prev->_next = node->_next;
if (_head == node) _head = node->_next;
if (_tail == node) _tail = node->_prev;
}
- (_YYLinkedMapNode *)removeTailNode {
if (!_tail) return nil;
_YYLinkedMapNode *tail = _tail;
CFDictionaryRemoveValue(_dic, (__bridge const void *)(_tail->_key));
_totalCost -= _tail->_cost;
_totalCount--;
if (_head == _tail) {
_head = _tail = nil;
} else {
_tail = _tail->_prev;
_tail->_next = nil;
}
return tail;
}
- (void)removeAll {
_totalCost = 0;
_totalCount = 0;
_head = nil;
_tail = nil;
if (CFDictionaryGetCount(_dic) > 0) {
CFMutableDictionaryRef holder = _dic;
_dic = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (_releaseAsynchronously) {
dispatch_queue_t queue = _releaseOnMainThread ? dispatch_get_main_queue() : YYMemoryCacheGetReleaseQueue();
dispatch_async(queue, ^{
CFRelease(holder); // hold and release in specified queue
});
} else if (_releaseOnMainThread && !pthread_main_np()) {
dispatch_async(dispatch_get_main_queue(), ^{
CFRelease(holder); // hold and release in specified queue
});
} else {
CFRelease(holder);
}
}
}
@end
@implementation YYMemoryCache {
pthread_mutex_t _lock;
_YYLinkedMap *_lru;
dispatch_queue_t _queue;
}
- (void)_trimRecursively {
__weak typeof(self) _self = self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_autoTrimInterval * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
__strong typeof(_self) self = _self;
if (!self) return;
[self _trimInBackground];
[self _trimRecursively];
});
}
- (void)_trimInBackground {
dispatch_async(_queue, ^{
[self _trimToCost:self->_costLimit];
[self _trimToCount:self->_countLimit];
[self _trimToAge:self->_ageLimit];
});
}
- (void)_trimToCost:(NSUInteger)costLimit {
BOOL finish = NO;
pthread_mutex_lock(&_lock);
if (costLimit == 0) {
[_lru removeAll];
finish = YES;
} else if (_lru->_totalCost <= costLimit) {
finish = YES;
}
pthread_mutex_unlock(&_lock);
if (finish) return;
NSMutableArray *holder = [NSMutableArray new];
while (!finish) {
if (pthread_mutex_trylock(&_lock) == 0) {
if (_lru->_totalCost > costLimit) {
_YYLinkedMapNode *node = [_lru removeTailNode];
if (node) [holder addObject:node];
} else {
finish = YES;
}
pthread_mutex_unlock(&_lock);
} else {
usleep(10 * 1000); //10 ms
}
}
if (holder.count) {
dispatch_queue_t queue = _lru->_releaseOnMainThread ? dispatch_get_main_queue() : YYMemoryCacheGetReleaseQueue();
dispatch_async(queue, ^{
[holder count]; // release in queue
});
}
}
- (void)_trimToCount:(NSUInteger)countLimit {
BOOL finish = NO;
pthread_mutex_lock(&_lock);
if (countLimit == 0) {
[_lru removeAll];
finish = YES;
} else if (_lru->_totalCount <= countLimit) {
finish = YES;
}
pthread_mutex_unlock(&_lock);
if (finish) return;
NSMutableArray *holder = [NSMutableArray new];
while (!finish) {
if (pthread_mutex_trylock(&_lock) == 0) {
if (_lru->_totalCount > countLimit) {
_YYLinkedMapNode *node = [_lru removeTailNode];
if (node) [holder addObject:node];
} else {
finish = YES;
}
pthread_mutex_unlock(&_lock);
} else {
usleep(10 * 1000); //10 ms
}
}
if (holder.count) {
dispatch_queue_t queue = _lru->_releaseOnMainThread ? dispatch_get_main_queue() : YYMemoryCacheGetReleaseQueue();
dispatch_async(queue, ^{
[holder count]; // release in queue
});
}
}
- (void)_trimToAge:(NSTimeInterval)ageLimit {
BOOL finish = NO;
NSTimeInterval now = CACurrentMediaTime();
pthread_mutex_lock(&_lock);
if (ageLimit <= 0) {
[_lru removeAll];
finish = YES;
} else if (!_lru->_tail || (now - _lru->_tail->_time) <= ageLimit) {
finish = YES;
}
pthread_mutex_unlock(&_lock);
if (finish) return;
NSMutableArray *holder = [NSMutableArray new];
while (!finish) {
if (pthread_mutex_trylock(&_lock) == 0) {
if (_lru->_tail && (now - _lru->_tail->_time) > ageLimit) {
_YYLinkedMapNode *node = [_lru removeTailNode];
if (node) [holder addObject:node];
} else {
finish = YES;
}
pthread_mutex_unlock(&_lock);
} else {
usleep(10 * 1000); //10 ms
}
}
if (holder.count) {
dispatch_queue_t queue = _lru->_releaseOnMainThread ? dispatch_get_main_queue() : YYMemoryCacheGetReleaseQueue();
dispatch_async(queue, ^{
[holder count]; // release in queue
});
}
}
- (void)_appDidReceiveMemoryWarningNotification {
if (self.didReceiveMemoryWarningBlock) {
self.didReceiveMemoryWarningBlock(self);
}
if (self.shouldRemoveAllObjectsOnMemoryWarning) {
[self removeAllObjects];
}
}
- (void)_appDidEnterBackgroundNotification {
if (self.didEnterBackgroundBlock) {
self.didEnterBackgroundBlock(self);
}
if (self.shouldRemoveAllObjectsWhenEnteringBackground) {
[self removeAllObjects];
}
}
#pragma mark - public
- (instancetype)init {
self = super.init;
pthread_mutex_init(&_lock, NULL);
_lru = [_YYLinkedMap new];
_queue = dispatch_queue_create("com.ibireme.cache.memory", DISPATCH_QUEUE_SERIAL);
_countLimit = NSUIntegerMax;
_costLimit = NSUIntegerMax;
_ageLimit = DBL_MAX;
_autoTrimInterval = 5.0;
_shouldRemoveAllObjectsOnMemoryWarning = YES;
_shouldRemoveAllObjectsWhenEnteringBackground = YES;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_appDidReceiveMemoryWarningNotification) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_appDidEnterBackgroundNotification) name:UIApplicationDidEnterBackgroundNotification object:nil];
[self _trimRecursively];
return self;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
[_lru removeAll];
pthread_mutex_destroy(&_lock);
}
- (NSUInteger)totalCount {
pthread_mutex_lock(&_lock);
NSUInteger count = _lru->_totalCount;
pthread_mutex_unlock(&_lock);
return count;
}
- (NSUInteger)totalCost {
pthread_mutex_lock(&_lock);
NSUInteger totalCost = _lru->_totalCost;
pthread_mutex_unlock(&_lock);
return totalCost;
}
- (BOOL)releaseOnMainThread {
pthread_mutex_lock(&_lock);
BOOL releaseOnMainThread = _lru->_releaseOnMainThread;
pthread_mutex_unlock(&_lock);
return releaseOnMainThread;
}
- (void)setReleaseOnMainThread:(BOOL)releaseOnMainThread {
pthread_mutex_lock(&_lock);
_lru->_releaseOnMainThread = releaseOnMainThread;
pthread_mutex_unlock(&_lock);
}
- (BOOL)releaseAsynchronously {
pthread_mutex_lock(&_lock);
BOOL releaseAsynchronously = _lru->_releaseAsynchronously;
pthread_mutex_unlock(&_lock);
return releaseAsynchronously;
}
- (void)setReleaseAsynchronously:(BOOL)releaseAsynchronously {
pthread_mutex_lock(&_lock);
_lru->_releaseAsynchronously = releaseAsynchronously;
pthread_mutex_unlock(&_lock);
}
- (BOOL)containsObjectForKey:(id)key {
if (!key) return NO;
pthread_mutex_lock(&_lock);
BOOL contains = CFDictionaryContainsKey(_lru->_dic, (__bridge const void *)(key));
pthread_mutex_unlock(&_lock);
return contains;
}
- (id)objectForKey:(id)key {
if (!key) return nil;
pthread_mutex_lock(&_lock);
_YYLinkedMapNode *node = CFDictionaryGetValue(_lru->_dic, (__bridge const void *)(key));
if (node) {
node->_time = CACurrentMediaTime();
[_lru bringNodeToHead:node];
}
pthread_mutex_unlock(&_lock);
return node ? node->_value : nil;
}
- (void)setObject:(id)object forKey:(id)key {
[self setObject:object forKey:key withCost:0];
}
- (void)setObject:(id)object forKey:(id)key withCost:(NSUInteger)cost {
if (!key) return;
if (!object) {
[self removeObjectForKey:key];
return;
}
pthread_mutex_lock(&_lock);
_YYLinkedMapNode *node = CFDictionaryGetValue(_lru->_dic, (__bridge const void *)(key));
NSTimeInterval now = CACurrentMediaTime();
if (node) {
_lru->_totalCost -= node->_cost;
_lru->_totalCost += cost;
node->_cost = cost;
node->_time = now;
node->_value = object;
[_lru bringNodeToHead:node];
} else {
node = [_YYLinkedMapNode new];
node->_cost = cost;
node->_time = now;
node->_key = key;
node->_value = object;
[_lru insertNodeAtHead:node];
}
if (_lru->_totalCost > _costLimit) {
dispatch_async(_queue, ^{
[self trimToCost:_costLimit];
});
}
if (_lru->_totalCount > _countLimit) {
_YYLinkedMapNode *node = [_lru removeTailNode];
if (_lru->_releaseAsynchronously) {
dispatch_queue_t queue = _lru->_releaseOnMainThread ? dispatch_get_main_queue() : YYMemoryCacheGetReleaseQueue();
dispatch_async(queue, ^{
[node class]; //hold and release in queue
});
} else if (_lru->_releaseOnMainThread && !pthread_main_np()) {
dispatch_async(dispatch_get_main_queue(), ^{
[node class]; //hold and release in queue
});
}
}
pthread_mutex_unlock(&_lock);
}
- (void)removeObjectForKey:(id)key {
if (!key) return;
pthread_mutex_lock(&_lock);
_YYLinkedMapNode *node = CFDictionaryGetValue(_lru->_dic, (__bridge const void *)(key));
if (node) {
[_lru removeNode:node];
if (_lru->_releaseAsynchronously) {
dispatch_queue_t queue = _lru->_releaseOnMainThread ? dispatch_get_main_queue() : YYMemoryCacheGetReleaseQueue();
dispatch_async(queue, ^{
[node class]; //hold and release in queue
});
} else if (_lru->_releaseOnMainThread && !pthread_main_np()) {
dispatch_async(dispatch_get_main_queue(), ^{
[node class]; //hold and release in queue
});
}
}
pthread_mutex_unlock(&_lock);
}
- (void)removeAllObjects {
pthread_mutex_lock(&_lock);
[_lru removeAll];
pthread_mutex_unlock(&_lock);
}
- (void)trimToCount:(NSUInteger)count {
if (count == 0) {
[self removeAllObjects];
return;
}
[self _trimToCount:count];
}
- (void)trimToCost:(NSUInteger)cost {
[self _trimToCost:cost];
}
- (void)trimToAge:(NSTimeInterval)age {
[self _trimToAge:age];
}
- (NSString *)description {
if (_name) return [NSString stringWithFormat:@"<%@: %p> (%@)", self.class, self, _name];
else return [NSString stringWithFormat:@"<%@: %p>", self.class, self];
}
@end