Skip to content

Commit

Permalink
merge from YYCache
Browse files Browse the repository at this point in the history
  • Loading branch information
ibireme committed Jun 13, 2016
1 parent d83616e commit 431f915
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 56 deletions.
4 changes: 4 additions & 0 deletions YYWebImage/Cache/YYDiskCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property NSTimeInterval autoTrimInterval;

/**
Set `YES` to enable error logs for debug.
*/
@property BOOL errorLogsEnabled;

#pragma mark - Initializer
///=============================================================================
Expand Down
26 changes: 26 additions & 0 deletions YYWebImage/Cache/YYDiskCache.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#import "YYDiskCache.h"
#import "YYKVStorage.h"
#import <UIKit/UIKit.h>
#import <CommonCrypto/CommonCrypto.h>
#import <objc/runtime.h>
#import <time.h>
Expand Down Expand Up @@ -149,8 +150,18 @@ - (NSString *)_filenameForKey:(NSString *)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];
Expand Down Expand Up @@ -193,6 +204,8 @@ - (instancetype)initWithPath:(NSString *)path

[self _trimRecursively];
_YYDiskCacheSetGlobal(self);

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_appWillBeTerminated) name:UIApplicationWillTerminateNotification object:nil];
return self;
}

Expand Down Expand Up @@ -429,4 +442,17 @@ - (NSString *)description {
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
126 changes: 75 additions & 51 deletions YYWebImage/Cache/YYKVStorage.m
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,29 @@
#import "sqlite3.h"
#endif


static const NSUInteger kMaxErrorRetryCount = 8;
static const NSTimeInterval kMinRetryTimeInterval = 2.0;
static const int kPathLengthMax = PATH_MAX - 64;
static NSString *const kDBFileName = @"manifest.sqlite";
static NSString *const kDBShmFileName = @"manifest.sqlite-shm";
static NSString *const kDBWalFileName = @"manifest.sqlite-wal";
static NSString *const kDataDirectoryName = @"data";
static NSString *const kTrashDirectoryName = @"trash";


/*
File:
/path/
/manifest.sqlite
/manifest.sqlite-shm
/manifest.sqlite-wal
/data/
/e10adc3949ba59abbe56e057f20f883e
/e10adc3949ba59abbe56e057f20f883e
/trash/
/unused_file_or_folder
SQL:
create table if not exists manifest (
key text,
Expand All @@ -41,6 +56,22 @@ primary key(key)
create index if not exists last_access_time_idx on manifest(last_access_time);
*/

/// Returns nil in App Extension.
static UIApplication *_YYSharedApplication() {
static BOOL isAppExtension = NO;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class cls = NSClassFromString(@"UIApplication");
if(!cls || ![cls respondsToSelector:@selector(sharedApplication)]) isAppExtension = YES;
if ([[[NSBundle mainBundle] bundlePath] hasSuffix:@".appex"]) isAppExtension = YES;
});
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
return isAppExtension ? nil : [UIApplication performSelector:@selector(sharedApplication)];
#pragma clang diagnostic pop
}


@implementation YYKVStorageItem
@end

Expand All @@ -54,49 +85,40 @@ @implementation YYKVStorage {

sqlite3 *_db;
CFMutableDictionaryRef _dbStmtCache;

BOOL _invalidated; ///< If YES, then the db should not open again, all read/write should be ignored.
BOOL _dbIsClosing; ///< If YES, then the db is during closing.
NSTimeInterval _dbLastOpenErrorTime;
NSUInteger _dbOpenErrorCount;
}


#pragma mark - db

- (BOOL)_dbOpen {
BOOL shouldOpen = YES;
if (_invalidated) {
shouldOpen = NO;
} else if (_dbIsClosing) {
shouldOpen = NO;
} else if (_db){
shouldOpen = NO;
}
if (!shouldOpen) return YES;
if (_db) return YES;

int result = sqlite3_open(_dbPath.UTF8String, &_db);
if (result == SQLITE_OK) {
CFDictionaryKeyCallBacks keyCallbacks = kCFCopyStringDictionaryKeyCallBacks;
CFDictionaryValueCallBacks valueCallbacks = {0};
_dbStmtCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &keyCallbacks, &valueCallbacks);
_dbLastOpenErrorTime = 0;
_dbOpenErrorCount = 0;
return YES;
} else {
NSLog(@"%s line:%d sqlite open failed (%d).", __FUNCTION__, __LINE__, result);
_db = NULL;
if (_dbStmtCache) CFRelease(_dbStmtCache);
_dbStmtCache = NULL;
_dbLastOpenErrorTime = CACurrentMediaTime();
_dbOpenErrorCount++;

if (_errorLogsEnabled) {
NSLog(@"%s line:%d sqlite open failed (%d).", __FUNCTION__, __LINE__, result);
}
return NO;
}
}

- (BOOL)_dbClose {
BOOL needClose = YES;
if (!_db) {
needClose = NO;
} else if (_invalidated) {
needClose = NO;
} else if (_dbIsClosing) {
needClose = NO;
} else {
_dbIsClosing = YES;
}
if (!needClose) return YES;
if (!_db) return YES;

int result = 0;
BOOL retry = NO;
Expand All @@ -118,16 +140,25 @@ - (BOOL)_dbClose {
}
}
} else if (result != SQLITE_OK) {
NSLog(@"%s line:%d sqlite close failed (%d).", __FUNCTION__, __LINE__, result);
if (_errorLogsEnabled) {
NSLog(@"%s line:%d sqlite close failed (%d).", __FUNCTION__, __LINE__, result);
}
}
} while (retry);
_db = NULL;
_dbIsClosing = NO;
return YES;
}

- (BOOL)_dbIsReady {
return (_db && !_dbIsClosing && !_invalidated);
- (BOOL)_dbCheck {
if (!_db) {
if (_dbOpenErrorCount < kMaxErrorRetryCount &&
CACurrentMediaTime() - _dbLastOpenErrorTime > kMinRetryTimeInterval) {
return [self _dbOpen] && [self _dbInitialize];
} else {
return NO;
}
}
return YES;
}

- (BOOL)_dbInitialize {
Expand All @@ -136,14 +167,14 @@ - (BOOL)_dbInitialize {
}

- (void)_dbCheckpoint {
if (![self _dbIsReady]) return;
if (![self _dbCheck]) return;
// Cause a checkpoint to occur, merge `sqlite-wal` file to `sqlite` file.
sqlite3_wal_checkpoint(_db, NULL);
}

- (BOOL)_dbExecute:(NSString *)sql {
if (sql.length == 0) return NO;
if (![self _dbIsReady]) return NO;
if (![self _dbCheck]) return NO;

char *error = NULL;
int result = sqlite3_exec(_db, sql.UTF8String, NULL, NULL, &error);
Expand All @@ -156,7 +187,7 @@ - (BOOL)_dbExecute:(NSString *)sql {
}

- (sqlite3_stmt *)_dbPrepareStmt:(NSString *)sql {
if (![self _dbIsReady] || sql.length == 0 || !_dbStmtCache) return NULL;
if (![self _dbCheck] || sql.length == 0 || !_dbStmtCache) 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);
Expand Down Expand Up @@ -230,7 +261,7 @@ - (BOOL)_dbUpdateAccessTimeWithKey:(NSString *)key {
}

- (BOOL)_dbUpdateAccessTimeWithKeys:(NSArray *)keys {
if (![self _dbIsReady]) return NO;
if (![self _dbCheck]) return NO;
int t = (int)time(NULL);
NSString *sql = [NSString stringWithFormat:@"update manifest set last_access_time = %d where key in (%@);", t, [self _dbJoinedKeys:keys]];

Expand Down Expand Up @@ -266,7 +297,7 @@ - (BOOL)_dbDeleteItemWithKey:(NSString *)key {
}

- (BOOL)_dbDeleteItemWithKeys:(NSArray *)keys {
if (![self _dbIsReady]) return NO;
if (![self _dbCheck]) return NO;
NSString *sql = [NSString stringWithFormat:@"delete from manifest where key in (%@);", [self _dbJoinedKeys:keys]];
sqlite3_stmt *stmt = NULL;
int result = sqlite3_prepare_v2(_db, sql.UTF8String, -1, &stmt, NULL);
Expand Down Expand Up @@ -353,7 +384,7 @@ - (YYKVStorageItem *)_dbGetItemWithKey:(NSString *)key excludeInlineData:(BOOL)e
}

- (NSMutableArray *)_dbGetItemWithKeys:(NSArray *)keys excludeInlineData:(BOOL)excludeInlineData {
if (![self _dbIsReady]) return nil;
if (![self _dbCheck]) return nil;
NSString *sql;
if (excludeInlineData) {
sql = [NSString stringWithFormat:@"select key, filename, size, modification_time, last_access_time, extended_data from manifest where key in (%@);", [self _dbJoinedKeys:keys]];
Expand Down Expand Up @@ -427,7 +458,7 @@ - (NSString *)_dbGetFilenameWithKey:(NSString *)key {
}

- (NSMutableArray *)_dbGetFilenameWithKeys:(NSArray *)keys {
if (![self _dbIsReady]) return nil;
if (![self _dbCheck]) return nil;
NSString *sql = [NSString stringWithFormat:@"select filename from manifest where key in (%@);", [self _dbJoinedKeys:keys]];
sqlite3_stmt *stmt = NULL;
int result = sqlite3_prepare_v2(_db, sql.UTF8String, -1, &stmt, NULL);
Expand Down Expand Up @@ -510,8 +541,8 @@ - (NSMutableArray *)_dbGetFilenamesWithTimeEarlierThan:(int)time {
return filenames;
}

- (NSMutableArray *)_dbGetItemSizeInfoOrderByTimeDescWithLimit:(int)count {
NSString *sql = @"select key, filename, size from manifest order by last_access_time desc limit ?1;";
- (NSMutableArray *)_dbGetItemSizeInfoOrderByTimeAscWithLimit:(int)count {
NSString *sql = @"select key, filename, size from manifest order by last_access_time asc limit ?1;";
sqlite3_stmt *stmt = [self _dbPrepareStmt:sql];
if (!stmt) return nil;
sqlite3_bind_int(stmt, 1, count);
Expand Down Expand Up @@ -580,26 +611,22 @@ - (int)_dbGetTotalItemCount {
#pragma mark - file

- (BOOL)_fileWriteWithName:(NSString *)filename data:(NSData *)data {
if (_invalidated) return NO;
NSString *path = [_dataPath stringByAppendingPathComponent:filename];
return [data writeToFile:path atomically:NO];
}

- (NSData *)_fileReadWithName:(NSString *)filename {
if (_invalidated) return nil;
NSString *path = [_dataPath stringByAppendingPathComponent:filename];
NSData *data = [NSData dataWithContentsOfFile:path];
return data;
}

- (BOOL)_fileDeleteWithName:(NSString *)filename {
if (_invalidated) return NO;
NSString *path = [_dataPath stringByAppendingPathComponent:filename];
return [[NSFileManager defaultManager] removeItemAtPath:path error:NULL];
}

- (BOOL)_fileMoveAllToTrash {
if (_invalidated) return NO;
CFUUIDRef uuidRef = CFUUIDCreate(NULL);
CFStringRef uuid = CFUUIDCreateString(NULL, uuidRef);
CFRelease(uuidRef);
Expand All @@ -613,7 +640,6 @@ - (BOOL)_fileMoveAllToTrash {
}

- (void)_fileEmptyTrashInBackground {
if (_invalidated) return;
NSString *trashPath = _trashPath;
dispatch_queue_t queue = _trashQueue;
dispatch_async(queue, ^{
Expand Down Expand Up @@ -641,10 +667,6 @@ - (void)_reset {
[self _fileEmptyTrashInBackground];
}

- (void)_appWillBeTerminated {
_invalidated = YES;
}

#pragma mark - public

- (instancetype)init {
Expand Down Expand Up @@ -698,13 +720,15 @@ - (instancetype)initWithPath:(NSString *)path type:(YYKVStorageType)type {
return nil;
}
[self _fileEmptyTrashInBackground]; // empty the trash if failed at last time
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_appWillBeTerminated) name:UIApplicationWillTerminateNotification object:nil];
return self;
}

- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillTerminateNotification object:nil];
UIBackgroundTaskIdentifier taskID = [_YYSharedApplication() beginBackgroundTaskWithExpirationHandler:^{}];
[self _dbClose];
if (taskID != UIBackgroundTaskInvalid) {
[_YYSharedApplication() endBackgroundTask:taskID];
}
}

- (BOOL)saveItem:(YYKVStorageItem *)item {
Expand Down Expand Up @@ -841,7 +865,7 @@ - (BOOL)removeItemsToFitSize:(int)maxSize {
BOOL suc = NO;
do {
int perCount = 16;
items = [self _dbGetItemSizeInfoOrderByTimeDescWithLimit:perCount];
items = [self _dbGetItemSizeInfoOrderByTimeAscWithLimit:perCount];
for (YYKVStorageItem *item in items) {
if (total > maxSize) {
if (item.filename) {
Expand Down Expand Up @@ -871,7 +895,7 @@ - (BOOL)removeItemsToFitCount:(int)maxCount {
BOOL suc = NO;
do {
int perCount = 16;
items = [self _dbGetItemSizeInfoOrderByTimeDescWithLimit:perCount];
items = [self _dbGetItemSizeInfoOrderByTimeAscWithLimit:perCount];
for (YYKVStorageItem *item in items) {
if (total > maxCount) {
if (item.filename) {
Expand Down Expand Up @@ -909,7 +933,7 @@ - (void)removeAllItemsWithProgressBlock:(void(^)(int removedCount, int totalCoun
NSArray *items = nil;
BOOL suc = NO;
do {
items = [self _dbGetItemSizeInfoOrderByTimeDescWithLimit:perCount];
items = [self _dbGetItemSizeInfoOrderByTimeAscWithLimit:perCount];
for (YYKVStorageItem *item in items) {
if (left > 0) {
if (item.filename) {
Expand Down
10 changes: 5 additions & 5 deletions YYWebImage/Cache/YYMemoryCache.m
Original file line number Diff line number Diff line change
Expand Up @@ -360,16 +360,16 @@ - (NSUInteger)totalCost {
return totalCost;
}

- (BOOL)releaseInMainThread {
- (BOOL)releaseOnMainThread {
pthread_mutex_lock(&_lock);
BOOL releaseInMainThread = _lru->_releaseOnMainThread;
BOOL releaseOnMainThread = _lru->_releaseOnMainThread;
pthread_mutex_unlock(&_lock);
return releaseInMainThread;
return releaseOnMainThread;
}

- (void)setReleaseInMainThread:(BOOL)releaseInMainThread {
- (void)setReleaseOnMainThread:(BOOL)releaseOnMainThread {
pthread_mutex_lock(&_lock);
_lru->_releaseOnMainThread = releaseInMainThread;
_lru->_releaseOnMainThread = releaseOnMainThread;
pthread_mutex_unlock(&_lock);
}

Expand Down

0 comments on commit 431f915

Please sign in to comment.