Browse Source

potentially problem in low memory device

ibireme 9 năm trước cách đây
mục cha
commit
ed2907cc73

+ 23 - 0
YYWebImage/Cache/YYCache.h

@@ -68,6 +68,29 @@ FOUNDATION_EXPORT const unsigned char YYCacheVersionString[];
  */
 - (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.
+ */
++ (instancetype)cacheWithName:(NSString *)name;
+
+/**
+ Convenience Initializers
+ Create a new instance with the specified name.
+ 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.
+ */
++ (instancetype)cacheWithPath:(NSString *)path;
+
 - (instancetype)init UNAVAILABLE_ATTRIBUTE;
 + (instancetype)new UNAVAILABLE_ATTRIBUTE;
 

+ 8 - 0
YYWebImage/Cache/YYCache.m

@@ -42,6 +42,14 @@
     return self;
 }
 
++ (instancetype)cacheWithName:(NSString *)name {
+	return [[YYCache alloc] initWithName:name];
+}
+
++ (instancetype)cacheWithPath:(NSString *)path {
+    return [[YYCache alloc] initWithPath:path];
+}
+
 - (BOOL)containsObjectForKey:(NSString *)key {
     return [_memoryCache containsObjectForKey:key] || [_diskCache containsObjectForKey:key];
 }

+ 2 - 2
YYWebImage/Cache/YYKVStorage.m

@@ -75,7 +75,7 @@ static NSString *const kTrashDirectoryName = @"trash";
     
     int result = sqlite3_open(_dbPath.UTF8String, &_db);
     if (result == SQLITE_OK) {
-        CFDictionaryKeyCallBacks keyCallbacks = kCFTypeDictionaryKeyCallBacks;
+        CFDictionaryKeyCallBacks keyCallbacks = kCFCopyStringDictionaryKeyCallBacks;
         CFDictionaryValueCallBacks valueCallbacks = {0};
         _dbStmtCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &keyCallbacks, &valueCallbacks);
         return YES;
@@ -156,7 +156,7 @@ static NSString *const kTrashDirectoryName = @"trash";
 }
 
 - (sqlite3_stmt *)_dbPrepareStmt:(NSString *)sql {
-    if (![self _dbIsReady]) 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);

+ 16 - 7
YYWebImage/Image/YYAnimatedImageView.m

@@ -135,7 +135,7 @@ typedef NS_ENUM(NSUInteger, YYAnimatedImageType) {
     NSUInteger _curIndex; ///< current frame index (from 0)
     NSUInteger _totalFrameCount; ///< total frame count
     
-    BOOL _loopEnd; ///< weather the loop is end.
+    BOOL _loopEnd; ///< whether the loop is end.
     NSUInteger _curLoop; ///< current loop count (from 0)
     NSUInteger _totalLoop; ///< total loop count, 0 means infinity
     
@@ -165,22 +165,28 @@ typedef NS_ENUM(NSUInteger, YYAnimatedImageType) {
     if ([self isCancelled]) return;
     view->_incrBufferCount++;
     if (view->_incrBufferCount == 0) [view calcMaxBufferCount];
-    if ((int)view->_incrBufferCount > (int)view->_maxBufferCount) {
+    if (view->_incrBufferCount > (NSInteger)view->_maxBufferCount) {
         view->_incrBufferCount = view->_maxBufferCount;
     }
     NSUInteger idx = _nextIndex;
     NSUInteger max = view->_incrBufferCount < 1 ? 1 : view->_incrBufferCount;
     NSUInteger total = view->_totalFrameCount;
+    view = nil;
+    
     for (int i = 0; i < max; i++, idx++) {
         @autoreleasepool {
             if (idx >= total) idx = 0;
             if ([self isCancelled]) break;
+            view = _view;
             LOCK_VIEW(BOOL miss = (view->_buffer[@(idx)] == nil));
+            view = nil;
             if (miss) {
                 UIImage *img = [_curImage animatedImageFrameAtIndex:idx];
-                img = [img yy_imageByDecoded];
+                img = img.yy_imageByDecoded;
                 if ([self isCancelled]) break;
+                view = _view;
                 LOCK_VIEW(view->_buffer[@(idx)] = img ? img : [NSNull null]);
+                view = nil;
             }
         }
     }
@@ -373,16 +379,18 @@ typedef NS_ENUM(NSUInteger, YYAnimatedImageType) {
 
 // dynamically adjust buffer size for current memory.
 - (void)calcMaxBufferCount {
-    NSUInteger bytes = _curAnimatedImage.animatedImageBytesPerFrame;
-    if (bytes == 0) bytes = 1;
+    int64_t bytes = (int64_t)_curAnimatedImage.animatedImageBytesPerFrame;
+    if (bytes == 0) bytes = 1024;
     
     int64_t total = _YYDeviceMemoryTotal();
     int64_t free = _YYDeviceMemoryFree();
     int64_t max = MIN(total * 0.2, free * 0.6);
     max = MAX(max, BUFFER_SIZE);
     if (_maxBufferSize) max = max > _maxBufferSize ? _maxBufferSize : max;
-    _maxBufferCount = (float)max / (float)bytes;
-    if (_maxBufferCount == 0) _maxBufferCount = 1;
+    double maxBufferCount = (double)max / (double)bytes;
+    if (maxBufferCount < 1) maxBufferCount = 1;
+    else if (maxBufferCount > 512) maxBufferCount = 512;
+    _maxBufferCount = maxBufferCount;
 }
 
 - (void)dealloc {
@@ -398,6 +406,7 @@ typedef NS_ENUM(NSUInteger, YYAnimatedImageType) {
 
 - (void)stopAnimating {
     [super stopAnimating];
+    [_requestQueue cancelAllOperations];
     _link.paused = YES;
     self.currentIsPlayingAnimation = NO;
 }

+ 5 - 3
YYWebImage/Image/YYImageCoder.m

@@ -1399,7 +1399,8 @@ CGImageRef YYCGImageCreateWithWebPData(CFDataRef webpData,
     config.output.u.RGBA.stride = (int)bytesPerRow;
     config.output.u.RGBA.size = destLength;
     
-    if (WebPDecode(payload, payloadSize, &config) != VP8_STATUS_OK) goto fail;
+    VP8StatusCode result = WebPDecode(payload, payloadSize, &config);
+    if ((result != VP8_STATUS_OK) && (result != VP8_STATUS_NOT_ENOUGH_DATA)) goto fail;
     
     if (iter.x_offset != 0 || iter.y_offset != 0) {
         void *tmp = calloc(1, destLength);
@@ -2150,7 +2151,8 @@ CGImageRef YYCGImageCreateWithWebPData(CFDataRef webpData,
         config.output.u.RGBA.rgba = pixels;
         config.output.u.RGBA.stride = (int)bytesPerRow;
         config.output.u.RGBA.size = length;
-        if (WebPDecode(payload, payloadSize, &config) != VP8_STATUS_OK) { // decode
+        VP8StatusCode result = WebPDecode(payload, payloadSize, &config); // decode
+        if ((result != VP8_STATUS_OK) && (result != VP8_STATUS_NOT_ENOUGH_DATA)) {
             WebPDemuxReleaseIterator(&iter);
             free(pixels);
             return NULL;
@@ -2724,7 +2726,7 @@ CGImageRef YYCGImageCreateWithWebPData(CFDataRef webpData,
 }
 
 + (NSData *)encodeImageWithDecoder:(YYImageDecoder *)decoder type:(YYImageType)type quality:(CGFloat)quality {
-    if (!decoder || !decoder.frameCount == 0) return nil;
+    if (!decoder || decoder.frameCount == 0) return nil;
     YYImageEncoder *encoder = [[YYImageEncoder alloc] initWithType:type];
     encoder.quality = quality;
     for (int i = 0; i < decoder.frameCount; i++) {