ibireme / yycache Goto Github PK
View Code? Open in Web Editor NEWHigh performance cache framework for iOS.
License: MIT License
High performance cache framework for iOS.
License: MIT License
Can you please provide an example for how to use YYCache ?
链表的单个节点和所有节点释放时,用boolean标明是否同步释放,如果NO应该是dispatch_sync(...)么?
源代码:
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(), ^{ <--------这里,不是应该用dispatch_sync?为啥咧~?
[node class]; //hold and release in queue
});
}
BenchMark里Value都是NSData:
NSData *value = [NSData dataWithBytes:&i length:sizeof(int)];
然后使用[yy setObject:values[i] forKey:keys[i]];
,在setObject:forKey:里:
- (void)setObject:(id)object forKey:(id)key {
[self setObject:object forKey:key withCost:0];
}
之后就没看见对cost的操作了。都是node直接复制node->_cost = cost
。
所以不太清楚cost累加的。
我自己写的Cache里,是对CFMutableDictionaryRef
转成NSMutableDictionary
然后KeyedArchive得到NSData然后算data.length来判断size。
如题,因为如果不同页面数据都放在一张表的话,key值有可能会有重复,所以想每个页面建一张表用于区分;同时,请问题如果放在了不同表中,如果进行访问;或者能不能以多个键值作为联合key检索数据
查看diskCache的相关代码:发现存取都要根据一个key来进行,假如我需要查询所有数据,该怎么做呢?
如图:
第一个红色的框应该直接return nil; 或者调用 initWithName: nil
第二个红色的框应该直接删掉
不过没什么问题,哈哈,就是偶尔扫了一眼
static const int kPathLengthMax = PATH_MAX - 64;
不明白减去64的作用是?大神求解...
有没有办法设置一些受保护的缓存文件,
后台清除过期文件的的时候,这些文件就算过期了(ageLimit),也避免被清除
因为 现在缓存的清除 完全是自动的
看到你有个这个TIP。那么你的YYImage的缓存又有引用你的YYCache的。那么也是同理相对会提高1-3倍效率吗
-(NSMutableArray *)_dbGetItemWithKeys:(NSArray *)keys excludeInlineData:(BOOL)excludeInlineData 方法中SQL语句
where key in (%@);
in 中的参数如果是字符串,需要单引号,否则stmt会失败。
1、看到内存缓存用的是双向链表,请问这么做只是用来按照时间排序的么?
2、cost值其实没看懂,请问cost是什么意思呢,看到赋值为0?
为什么内存缓存中使用pthread_mutex_t,进行加锁而磁盘缓存中使用dispatch_semaphore_t信号量进行加锁呢?希望能解答疑惑,拜托拜托
Lately I've been read this source code, and then i found some mistakes in the comments(注释),in the code you mean you wanna invoke some method or some block, but you use the word 'revoke',which means 取消、撤销.
YY大神说YYCache使用了LRU算法, 但是我阅读了下源码, 只发现了维护链表头为最近使用的节点, 但是没有发现对访问频率的统计???还是其它地方实现了等效的功能, 比较疑惑.
使用第三方的ffmpeg中有time.h文件,这样会和YYCache中使用的time()函数冲突,导致这样的错误
YYKVStorage.m:201:26: Implicit declaration of function 'time' is invalid in C99
Declaration of 'time' must be imported from module 'Darwin.C.time' before it is required
,我查看工程中ffmpeg中这个time.h并没有使用,暂时处理方式是删除这个time.h的引用。
问一下,有没有更好的处理方式 ?毕竟我的处理方式并不是一个真正解决问题的方法!
setObject: forKey: withBlock not work... but setObject: forKey: works ,why?
MemoryCache中用pthread_mutex_lock锁,
DiskCache中对数据库操作部分用的GCD的信号量。
请问为什么这样选择的?有什么原则吗?
在你的应用中,使用了mutex来做为线程锁。在init方法中,你创建了一个mutex,但是没有任何地方释放此mutex,你应该在dealloc或别的适当的地方使用pthread_mutex_destroy(&_lock)来释放这个创建的mutex。
for example: SQLCipher support
我改了下demo,用并发线程进行读的时候会crash
写的时候sqlite有文件锁,是正常的。
请问YYCache能否存储大量照片,把所有的照片压缩成zip,然后上传到服务器?
在个人中心里面清空了所有的Cache,但在进入收藏的时候,个人资料缓存还是没有清空,请问有没可以立即同步的选项?
我用YYCache做了登录之后的个人资料,cookie的保存. 然后,我在每次请求数据的时候,将保存的数据加入到请求头(cookie)里面,用来验证用户.
但我在别的页面清楚所有的Cache之后(RemoveAll),再请求数据发现,并没有清除掉, 用户信息还是保留着.
所以,我想问一下有没有立即执行的选项.
A timeout optional parameters when store a value for key could be a nice addition to this lib. Thanks.
加载数据时先根据失效时间判断缓存是否有效,失效则从新请求数据,可以吗?
你好,感谢你分享的YYCache项目,我们在项目中有使用,在使用过程中我们发现一个问题:
_dbOpen函数在某些情况下由于打开sqlite数据库失败,但是内部状态维护有点问题,具体是场景是:图片缓存被多次清理,在某次清理时打开sqlite数据库返回SQLITE_CANTOPEN,_dbOpen函数返回了NO,但是内部状态,db,_invalidated等状态都标示为打开成功状态,在下次使用图片缓存时由于_dbStmtCache被置为NULL,导致crash。
这里存在的问题还和sqlite的API有关系,int result = sqlite3_open(_dbPath.UTF8String, &_db);在返回失败是,db被赋值了
I'm looking for key value store solution on iOS. Currently I'm using YTKKeyValueStore, which is not maintained for a year and contains some bugs. I'm hoping YYKVStorage
can be separated into another independant project, so that it can be used for general key-value store usage.
if (node) {
[_lru removeNode:node];
_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
});
}
}
我最近在学习 iOS 的内存管理知识,这个地方我有一些不解,想请教一下您。
_YYLinkedMapNode *node = [_lru removeTailNode];
这里的 node 是被注册到了 autoreleasepool 吧?
在接下来的 dispatch_async GCD 操作是为了什么呢?
不是会有 ARC 来帮助自动 release 没有用的内存么?
还是说想根据自定义的需求,指定在某个线程下完成释放?但是在 autoreleasepool 中的释放并不是立即进行的么?
你好,YYKVStorage.m 中获取最近访问数据时,是否应该使用 asc
而不是 desc
。最近的时间值更大。使用 desc 导致每次移除时把最近使用的元素优先移除掉了。
- (BOOL)removeItemsToFitCount:(int)maxCount {
items = [self _dbGetItemSizeInfoOrderByTimeDescWithLimit:perCount];
- (NSMutableArray *)_dbGetItemSizeInfoOrderByTimeDescWithLimit:(int)count {
NSString *sql = @"select key, filename, size from manifest order by last_access_time desc limit ?1;";
sqlite3_stmt *stmt = [self _dbPrepareStmt:sql];
测试代码如下:
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSString *docDir = [(NSArray*)NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)firstObject];
NSString *testDir = [docDir stringByAppendingPathComponent:@"testDir"];
NSLog(@"testDir = %@", testDir);
YYDiskCache *diskCache = [[YYDiskCache alloc]initWithPath:testDir inlineThreshold:0];
diskCache.customFileNameBlock = ^(NSString *key){
return [key stringByAppendingPathExtension:@"txt"];
};
[diskCache setObject:@"hello" forKey:@"1"];
[diskCache setObject:@"world" forKey:@"2"];
NSLog(@"total count : %@",@([diskCache totalCount]));
// NSLog(@"key 1 = %@", [diskCache objectForKey:@"1"]);
// usleep(1000000);
// NSLog(@"key 2 = %@", [diskCache objectForKey:@"2"]);
NSLog(@"key 2 = %@", [diskCache objectForKey:@"2"]);
usleep(1000000);
NSLog(@"key 1 = %@", [diskCache objectForKey:@"1"]);
[diskCache trimToCount:1];
NSLog(@"total count : %@",@([diskCache totalCount]));
NSLog(@"key 1 = %@", [diskCache objectForKey:@"1"]);
NSLog(@"key 2 = %@", [diskCache objectForKey:@"2"]);
});
输出如下:
2016-03-28 00:47:00.696 YYCacheTest[55228:1046537] total count : 2
2016-03-28 00:47:00.697 YYCacheTest[55228:1046537] key 2 = world
2016-03-28 00:47:01.700 YYCacheTest[55228:1046537] key 1 = hello
2016-03-28 00:47:01.701 YYCacheTest[55228:1046537] total count : 1
2016-03-28 00:47:01.701 YYCacheTest[55228:1046537] key 1 = (null)
2016-03-28 00:47:01.702 YYCacheTest[55228:1046537] key 2 = world
在最近访问 key1之后,应该优先移除 key2。但目前是移除了key1的值。
Your work is so fantastic.
Line 21:It use LRU (least-recently-used) to remove objects; NSCache's eviction method is non-deterministic.
It should be It uses balabala.
:)
不知这样算不算伸手党,,
但不知道该如何来编译并加入工程中。
YY大神, 我是这样理解的, countLimit是从节点的个数这个粒度上来限制缓存大小.
但是costLimit是从哪个纬度上来细分的呢?还是我理解得有错误?
The file creation part from YYKVStorage.m:
NSError *error = nil;
if (![[NSFileManager defaultManager] createDirectoryAtPath:path
withIntermediateDirectories:YES
attributes:nil
error:&error] ||
![[NSFileManager defaultManager] createDirectoryAtPath:[path stringByAppendingPathComponent:kDataDirectoryName]
withIntermediateDirectories:YES
attributes:nil
error:&error] ||
![[NSFileManager defaultManager] createDirectoryAtPath:[path stringByAppendingPathComponent:kTrashDirectoryName]
withIntermediateDirectories:YES
attributes:nil
error:&error]) {
NSLog(@"YYKVStorage init error:%@", error);
return nil;
}
This will not result in an error if directory already exists.
(void)_trimToCost:(NSUInteger)costLimit {
BOOL finish = NO;
OSSpinLockLock(&_lock);
if (costLimit == 0) {
[_lru removeAll];
finish = YES;
} else if (_lru->_totalCost <= costLimit) {
finish = YES;
}
OSSpinLockUnlock(&_lock);
if (finish) return;
NSMutableArray *holder = [NSMutableArray new];
while (!finish) {
if (OSSpinLockTry(&_lock)) {
if (_lru->_totalCost > costLimit) {
_YYLinkedMapNode *node = [_lru removeTailNode];
if (node) [holder addObject:node];
} else {
finish = YES;
}
OSSpinLockUnlock(&_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
});
}
}
这段代码,最后释放数组的时候,调用数组的count方法,怎么会就释放了数组了呢?
能单独设置每个缓存的存活时间吗?
在我的业务中,需要知道 file 的 path,但好似 Storage 里面才有 path, YYCache 不能获取
另外,我想问一下,如何解决 imageIO_PNG_data 不断增大的问题,png 图片设置后,无法在内存中释放
YY 大神你好,最近我也在完善我自己写的一个 SwiftCache,在对比测试各个 cache 库时,发现了一个问题,不知是有意为之增加同步效率,还是疏漏。
- (void)setObject:(id)object forKey:(id)key withCost:(NSUInteger)cost {
...
if (_lru->_totalCost > _costLimit) {
dispatch_async(_queue, ^{
[self trimToCost:_costLimit];
});
}
...
}
具体问题在 memoryCache 的同步写入的操作(上述代码),同步写入完成之后,有一个淘汰的过程,这时在淘汰 cost 时采用的是异步主线程淘汰,潜在问题在于如果紧接着做同步的读 cache,可能会出现异步主线程的 cost 淘汰还没有执行的情况。
我试图去更改您的代码,想要 PR,如下:
- (void)setObject:(id)object forKey:(id)key withCost:(NSUInteger)cost {
...
pthread_mutex_unlock(&_lock); // 这里解锁,因为 trimToCost 中有锁
if (_lru->_totalCost > _costLimit) {
// 去掉异步 trim
[self trimToCost:_costLimit];
}
pthread_mutex_lock(&_lock); // 这里再次加锁
...
}
但发现 if 中的 _lru _totalCost _costLimit 又变成了非线程安全,所以可能需要改的地方比较多,希望您看下是是否需要更正。
不胜感激。
作者你好,在使用YYDiskCache时,再次初始化YYDiskCache,传入原有路径会清除原有数据?这种情况如何再次初始化原有路径的老数据呢?或者有其他使用方法?谢谢。
比如我写了个Model,我想用YYCache来缓存,但是提示警告
[TestModel encodeWithCoder:]: unrecognized selector sent to instance 0x15e4ac6c0
2016-03-21 16:47:43.069 WisdomCommunity[2247:640281] *** -[NSKeyedArchiver dealloc]: warning: NSKeyedArchiver deallocated without having had -finishEncoding called on it.
求指导方法
我看到此方法的实现里这样调用:
[_lru removeNode:node];
_YYLinkedMapNode *node = [_lru removeTailNode];
请问除了将传入的node删除后,为何需要再删除一次尾结点?谢谢!
清理磁盘存储的时候,经常会崩溃在这一句。
sqlite3_stmt *stmt = (sqlite3_stmt *)CFDictionaryGetValue(_dbStmtCache, (__bridge const void *)(sql));
具体在下面这个函数中,报错是Thread34:EXC_BAD_ACCESS
- (sqlite3_stmt *)_dbPrepareStmt:(NSString *)sql { if (![self _dbIsReady]) return NULL; sqlite3_stmt *stmt = (sqlite3_stmt *)CFDictionaryGetValue(_dbStmtCache, (__bridge const void *)(sql)); if (!stmt) { int result = sqlite3_prepare_v2(_db, sql.UTF8String, -1, &stmt, NULL); if (result != SQLITE_OK) { if (_errorLogsEnabled) NSLog(@"%s line:%d sqlite stmt prepare error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db)); return NULL; } CFDictionarySetValue(_dbStmtCache, (__bridge const void *)(sql), stmt); } else { sqlite3_reset(stmt); } return stmt; }
YYCache可以存模型数组吗?应该怎么存??
YY大神,我在看源码的时候发现在YYDiskCache类中有大量的操作是有withBlock的,而YYMemoryCache中同样的几个方法是没有的,这是为什么呀?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.