diff --git a/Framework/Info.plist b/Framework/Info.plist index e2ecf82..e52b996 100644 --- a/Framework/Info.plist +++ b/Framework/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 1.0.3 + 1.0.4 CFBundleSignature ???? CFBundleVersion diff --git a/YYWebImage.podspec b/YYWebImage.podspec index e45217f..22bd9f7 100644 --- a/YYWebImage.podspec +++ b/YYWebImage.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = 'YYWebImage' s.summary = 'Asynchronous image loading framework.' - s.version = '1.0.3' + s.version = '1.0.4' s.license = { :type => 'MIT', :file => 'LICENSE' } s.authors = { 'ibireme' => 'ibireme@gmail.com' } s.social_media_url = 'http://blog.ibireme.com' diff --git a/YYWebImage/Cache/YYDiskCache.h b/YYWebImage/Cache/YYDiskCache.h index dd193e4..d61c621 100644 --- a/YYWebImage/Cache/YYDiskCache.h +++ b/YYWebImage/Cache/YYDiskCache.h @@ -131,10 +131,6 @@ NS_ASSUME_NONNULL_BEGIN */ @property NSTimeInterval autoTrimInterval; -/** - Set `YES` to enable error logs for debug. - */ -@property BOOL errorLogsEnabled; #pragma mark - Initializer ///============================================================================= diff --git a/YYWebImage/Cache/YYDiskCache.m b/YYWebImage/Cache/YYDiskCache.m index 735cc5d..77d18c8 100644 --- a/YYWebImage/Cache/YYDiskCache.m +++ b/YYWebImage/Cache/YYDiskCache.m @@ -11,7 +11,6 @@ #import "YYDiskCache.h" #import "YYKVStorage.h" -#import #import #import #import @@ -150,18 +149,8 @@ - (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]; @@ -204,8 +193,6 @@ - (instancetype)initWithPath:(NSString *)path [self _trimRecursively]; _YYDiskCacheSetGlobal(self); - - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_appWillBeTerminated) name:UIApplicationWillTerminateNotification object:nil]; return self; } @@ -442,17 +429,4 @@ - (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 diff --git a/YYWebImage/Cache/YYKVStorage.m b/YYWebImage/Cache/YYKVStorage.m index f3587b0..5ea4e27 100644 --- a/YYWebImage/Cache/YYKVStorage.m +++ b/YYWebImage/Cache/YYKVStorage.m @@ -19,9 +19,6 @@ #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"; @@ -29,19 +26,7 @@ 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, @@ -56,22 +41,6 @@ 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 @@ -85,40 +54,49 @@ @implementation YYKVStorage { sqlite3 *_db; CFMutableDictionaryRef _dbStmtCache; - NSTimeInterval _dbLastOpenErrorTime; - NSUInteger _dbOpenErrorCount; + + 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. } #pragma mark - db - (BOOL)_dbOpen { - if (_db) return YES; + BOOL shouldOpen = YES; + if (_invalidated) { + shouldOpen = NO; + } else if (_dbIsClosing) { + shouldOpen = NO; + } else if (_db){ + shouldOpen = NO; + } + if (!shouldOpen) 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 { - _db = NULL; - if (_dbStmtCache) CFRelease(_dbStmtCache); - _dbStmtCache = NULL; - _dbLastOpenErrorTime = CACurrentMediaTime(); - _dbOpenErrorCount++; - - if (_errorLogsEnabled) { - NSLog(@"%s line:%d sqlite open failed (%d).", __FUNCTION__, __LINE__, result); - } + NSLog(@"%s line:%d sqlite open failed (%d).", __FUNCTION__, __LINE__, result); return NO; } } - (BOOL)_dbClose { - if (!_db) return YES; + BOOL needClose = YES; + if (!_db) { + needClose = NO; + } else if (_invalidated) { + needClose = NO; + } else if (_dbIsClosing) { + needClose = NO; + } else { + _dbIsClosing = YES; + } + if (!needClose) return YES; int result = 0; BOOL retry = NO; @@ -140,25 +118,16 @@ - (BOOL)_dbClose { } } } else if (result != SQLITE_OK) { - if (_errorLogsEnabled) { - NSLog(@"%s line:%d sqlite close failed (%d).", __FUNCTION__, __LINE__, result); - } + NSLog(@"%s line:%d sqlite close failed (%d).", __FUNCTION__, __LINE__, result); } } while (retry); _db = NULL; + _dbIsClosing = NO; return YES; } -- (BOOL)_dbCheck { - if (!_db) { - if (_dbOpenErrorCount < kMaxErrorRetryCount && - CACurrentMediaTime() - _dbLastOpenErrorTime > kMinRetryTimeInterval) { - return [self _dbOpen] && [self _dbInitialize]; - } else { - return NO; - } - } - return YES; +- (BOOL)_dbIsReady { + return (_db && !_dbIsClosing && !_invalidated); } - (BOOL)_dbInitialize { @@ -167,14 +136,14 @@ - (BOOL)_dbInitialize { } - (void)_dbCheckpoint { - if (![self _dbCheck]) return; + if (![self _dbIsReady]) 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 _dbCheck]) return NO; + if (![self _dbIsReady]) return NO; char *error = NULL; int result = sqlite3_exec(_db, sql.UTF8String, NULL, NULL, &error); @@ -187,7 +156,7 @@ - (BOOL)_dbExecute:(NSString *)sql { } - (sqlite3_stmt *)_dbPrepareStmt:(NSString *)sql { - if (![self _dbCheck] || sql.length == 0 || !_dbStmtCache) return NULL; + if (![self _dbIsReady] || 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); @@ -261,7 +230,7 @@ - (BOOL)_dbUpdateAccessTimeWithKey:(NSString *)key { } - (BOOL)_dbUpdateAccessTimeWithKeys:(NSArray *)keys { - if (![self _dbCheck]) return NO; + if (![self _dbIsReady]) 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]]; @@ -297,7 +266,7 @@ - (BOOL)_dbDeleteItemWithKey:(NSString *)key { } - (BOOL)_dbDeleteItemWithKeys:(NSArray *)keys { - if (![self _dbCheck]) return NO; + if (![self _dbIsReady]) 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); @@ -384,7 +353,7 @@ - (YYKVStorageItem *)_dbGetItemWithKey:(NSString *)key excludeInlineData:(BOOL)e } - (NSMutableArray *)_dbGetItemWithKeys:(NSArray *)keys excludeInlineData:(BOOL)excludeInlineData { - if (![self _dbCheck]) return nil; + if (![self _dbIsReady]) 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]]; @@ -458,7 +427,7 @@ - (NSString *)_dbGetFilenameWithKey:(NSString *)key { } - (NSMutableArray *)_dbGetFilenameWithKeys:(NSArray *)keys { - if (![self _dbCheck]) return nil; + if (![self _dbIsReady]) 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); @@ -541,8 +510,8 @@ - (NSMutableArray *)_dbGetFilenamesWithTimeEarlierThan:(int)time { return filenames; } -- (NSMutableArray *)_dbGetItemSizeInfoOrderByTimeAscWithLimit:(int)count { - NSString *sql = @"select key, filename, size from manifest order by last_access_time asc limit ?1;"; +- (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]; if (!stmt) return nil; sqlite3_bind_int(stmt, 1, count); @@ -611,22 +580,26 @@ - (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); @@ -640,6 +613,7 @@ - (BOOL)_fileMoveAllToTrash { } - (void)_fileEmptyTrashInBackground { + if (_invalidated) return; NSString *trashPath = _trashPath; dispatch_queue_t queue = _trashQueue; dispatch_async(queue, ^{ @@ -667,6 +641,10 @@ - (void)_reset { [self _fileEmptyTrashInBackground]; } +- (void)_appWillBeTerminated { + _invalidated = YES; +} + #pragma mark - public - (instancetype)init { @@ -720,15 +698,13 @@ - (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 { - UIBackgroundTaskIdentifier taskID = [_YYSharedApplication() beginBackgroundTaskWithExpirationHandler:^{}]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillTerminateNotification object:nil]; [self _dbClose]; - if (taskID != UIBackgroundTaskInvalid) { - [_YYSharedApplication() endBackgroundTask:taskID]; - } } - (BOOL)saveItem:(YYKVStorageItem *)item { @@ -865,7 +841,7 @@ - (BOOL)removeItemsToFitSize:(int)maxSize { BOOL suc = NO; do { int perCount = 16; - items = [self _dbGetItemSizeInfoOrderByTimeAscWithLimit:perCount]; + items = [self _dbGetItemSizeInfoOrderByTimeDescWithLimit:perCount]; for (YYKVStorageItem *item in items) { if (total > maxSize) { if (item.filename) { @@ -895,7 +871,7 @@ - (BOOL)removeItemsToFitCount:(int)maxCount { BOOL suc = NO; do { int perCount = 16; - items = [self _dbGetItemSizeInfoOrderByTimeAscWithLimit:perCount]; + items = [self _dbGetItemSizeInfoOrderByTimeDescWithLimit:perCount]; for (YYKVStorageItem *item in items) { if (total > maxCount) { if (item.filename) { @@ -933,7 +909,7 @@ - (void)removeAllItemsWithProgressBlock:(void(^)(int removedCount, int totalCoun NSArray *items = nil; BOOL suc = NO; do { - items = [self _dbGetItemSizeInfoOrderByTimeAscWithLimit:perCount]; + items = [self _dbGetItemSizeInfoOrderByTimeDescWithLimit:perCount]; for (YYKVStorageItem *item in items) { if (left > 0) { if (item.filename) { diff --git a/YYWebImage/Cache/YYMemoryCache.m b/YYWebImage/Cache/YYMemoryCache.m index 9042d9b..5c078b2 100644 --- a/YYWebImage/Cache/YYMemoryCache.m +++ b/YYWebImage/Cache/YYMemoryCache.m @@ -360,16 +360,16 @@ - (NSUInteger)totalCost { return totalCost; } -- (BOOL)releaseOnMainThread { +- (BOOL)releaseInMainThread { pthread_mutex_lock(&_lock); - BOOL releaseOnMainThread = _lru->_releaseOnMainThread; + BOOL releaseInMainThread = _lru->_releaseOnMainThread; pthread_mutex_unlock(&_lock); - return releaseOnMainThread; + return releaseInMainThread; } -- (void)setReleaseOnMainThread:(BOOL)releaseOnMainThread { +- (void)setReleaseInMainThread:(BOOL)releaseInMainThread { pthread_mutex_lock(&_lock); - _lru->_releaseOnMainThread = releaseOnMainThread; + _lru->_releaseOnMainThread = releaseInMainThread; pthread_mutex_unlock(&_lock); } diff --git a/YYWebImage/Image/YYAnimatedImageView.h b/YYWebImage/Image/YYAnimatedImageView.h index 7412278..e70a654 100644 --- a/YYWebImage/Image/YYAnimatedImageView.h +++ b/YYWebImage/Image/YYAnimatedImageView.h @@ -11,8 +11,6 @@ #import -@protocol YYAnimatedImage; - NS_ASSUME_NONNULL_BEGIN /** @@ -82,12 +80,6 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nonatomic) NSUInteger maxBufferSize; -/** - * A convenience accessor for the getting the animated image that the image view is currently displaying - * If the `image` property does not contain an animated image, this property returns nil. - */ -@property (nonatomic, strong, nullable) UIImage *animatedImage; - @end diff --git a/YYWebImage/Image/YYAnimatedImageView.m b/YYWebImage/Image/YYAnimatedImageView.m index 7072fc8..5f2bcb4 100644 --- a/YYWebImage/Image/YYAnimatedImageView.m +++ b/YYWebImage/Image/YYAnimatedImageView.m @@ -301,20 +301,6 @@ - (void)setHighlighted:(BOOL)highlighted { [self imageChanged]; } -- (void)setAnimatedImage:(UIImage *)animatedImage { - if (self.image == animatedImage) { - return; - } - [self setImage:animatedImage withType:YYAnimatedImageTypeImage]; -} - -- (UIImage *)animatedImage { - if (![self.image conformsToProtocol:@protocol(YYAnimatedImage)]) { - return nil; - } - return (UIImage *)self.image; -} - - (id)imageForType:(YYAnimatedImageType)type { switch (type) { case YYAnimatedImageTypeNone: return nil;