1. 程式人生 > >AFNetWorking 和 SDWebImage 圖片快取對比

AFNetWorking 和 SDWebImage 圖片快取對比

文章 篇幅 較長,其中有很多對比原始碼的地方。如果沒有耐心看分析,可以直接看 最後的結論。

首先 提下: NSURLCache   和 NSCache。

NSURLCache: 可以在memory 和 disk 上快取。但是在memory上快取時,沒有自動清理機制。單純性的拷貝到memory上。( BTW: 網上很多對比人說AFNetwork的圖片快取使用了NSURLCache,但是 經過筆者研究最新版本的AFNetwork 並沒有使用NSURLCache。反而在SDWebImage 中的網路請求中使用了。但SDWebImage 並不是用NSURLCache來實現磁碟快取的。

            NSCache: 在memory上快取,類似於NSMutableDictionary ,以 雜湊演算法 管理。有自動清理機制,當快取到memory時,如果memory空間不夠,則會自動刪除memory中當前介面不使用的空間。SDWebImage 中的圖片快取  在memory上使用的是 NSCache,在disk 是則用到的是  NSFileManager。AFNetWorking 的圖片快取,在在memory上使用的是 NSCache,沒有磁碟快取。

接下來  通過原始碼 來 對比 AFNetWorking 和 SDWebImage 圖片快取

AFNetWorking 最新版:(2015-07-28 )


UIImageView+AFNetworking.m

- (void)setImageWithURL:(NSURL *)url {
    [self setImageWithURL:url placeholderImage:nil];
}

- (void)setImageWithURL:(NSURL *)url
       placeholderImage:(UIImage *)placeholderImage
{
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    [request addValue:@"image/*" forHTTPHeaderField:@"Accept"];

    [self setImageWithURLRequest:request placeholderImage:placeholderImage success:nil failure:nil];
}

- (void)setImageWithURLRequest:(NSURLRequest *)urlRequest
              placeholderImage:(UIImage *)placeholderImage
                       success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image))success
                       failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error))failure
{
    [self cancelImageRequestOperation];

    <span style="color:#ff0000;">UIImage *cachedImage = [[[self class] sharedImageCache] cachedImageForRequest:urlRequest];</span>
    if (cachedImage) {
        if (success) {
            success(nil, nil, cachedImage);
        } else {
            self.image = cachedImage;
        }

        self.af_imageRequestOperation = nil;
    } else {
        if (placeholderImage) {
            self.image = placeholderImage;
        }

        __weak __typeof(self)weakSelf = self;
        self.af_imageRequestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:urlRequest];
        self.af_imageRequestOperation.responseSerializer = self.imageResponseSerializer;
        [self.af_imageRequestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
            __strong __typeof(weakSelf)strongSelf = weakSelf;
            if ([[urlRequest URL] isEqual:[strongSelf.af_imageRequestOperation.request URL]]) {
                if (success) {
                    success(urlRequest, operation.response, responseObject);
                } else if (responseObject) {
                    strongSelf.image = responseObject;
                }

                if (operation == strongSelf.af_imageRequestOperation){
                        strongSelf.af_imageRequestOperation = nil;
                }
            }

            [[[strongSelf class] sharedImageCache] cacheImage:responseObject forRequest:urlRequest];
        } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
            __strong __typeof(weakSelf)strongSelf = weakSelf;
            if ([[urlRequest URL] isEqual:[strongSelf.af_imageRequestOperation.request URL]]) {
                if (failure) {
                    failure(urlRequest, operation.response, error);
                }

                if (operation == strongSelf.af_imageRequestOperation){
                        strongSelf.af_imageRequestOperation = nil;
                }
            }
        }];

        [[[self class] af_sharedImageRequestOperationQueue] addOperation:self.af_imageRequestOperation];
    }
}

UIImage *cachedImage = [[[selfclass] sharedImageCache]cachedImageForRequest:urlRequest];

這句即是查詢快取,那麼我們到   AFImageCache 中來看看:
@interface AFImageCache : NSCache <AFImageCache>
@end

可見 AFImageCache 繼承自NSCache. 所以 AFNetWorking 在memory中的快取使用了(cocoa kit提供的)NSCache。

如果 沒有找到memory中的圖片 就開始使用:

 self.af_imageRequestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:urlRequest];
        self.af_imageRequestOperation.responseSerializer = self.imageResponseSerializer;

開始網路請求。再來看看 這個 AFHTTPRequestOperation

@interface AFHTTPRequestOperation : AFURLConnectionOperation
而  AFURLConnectionOperation
@interface AFURLConnectionOperation : NSOperation <NSURLConnectionDelegate, NSURLConnectionDataDelegate, NSSecureCoding, NSCopying>
我們知道  AFURLConnectionOperation 即是使用了  NSURLConnection  進行的網路請求。
@interface AFURLConnectionOperation ()
@property (readwrite, nonatomic, assign) AFOperationState state;
@property (readwrite, nonatomic, strong) NSRecursiveLock *lock;
<span style="color:#ff0000;">@property (readwrite, nonatomic, strong) NSURLConnection *connection;
@property (readwrite, nonatomic, strong) NSURLRequest *request;
@property (readwrite, nonatomic, strong) NSURLResponse *response;</span>
@property (readwrite, nonatomic, strong) NSError *error;
@property (readwrite, nonatomic, strong) NSData *responseData;

來看看   initWithRequest 這個方法,到底 AFNetWorking 是使沒使用 NSURLCache 快取。
- (instancetype)initWithRequest:(NSURLRequest *)urlRequest {
    NSParameterAssert(urlRequest);

    self = [super init];
    if (!self) {
		return nil;
    }

    _state = AFOperationReadyState;

    self.lock = [[NSRecursiveLock alloc] init];
    self.lock.name = kAFNetworkingLockName;

    self.runLoopModes = [NSSet setWithObject:NSRunLoopCommonModes];

    self.request = urlRequest;

    self.shouldUseCredentialStorage = YES;

    self.securityPolicy = [AFSecurityPolicy defaultPolicy];

    return self;
}

沒有。根本就沒有使用 NSURLCache 。再看看 最後獲取圖片data 完成後進行了什麼操作:
[self.af_imageRequestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
            __strong __typeof(weakSelf)strongSelf = weakSelf;
            if ([[urlRequest URL] isEqual:[strongSelf.af_imageRequestOperation.request URL]]) {
                if (success) {
                    success(urlRequest, operation.response, responseObject);
                } else if (responseObject) {
                    strongSelf.image = responseObject;
                }

                if (operation == strongSelf.af_imageRequestOperation){
                        strongSelf.af_imageRequestOperation = nil;
                }
            }

           <span style="color:#ff0000;"> [[[strongSelf class] sharedImageCache] cacheImage:responseObject forRequest:urlRequest];</span>
        } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
            __strong __typeof(weakSelf)strongSelf = weakSelf;
            if ([[urlRequest URL] isEqual:[strongSelf.af_imageRequestOperation.request URL]]) {
                if (failure) {
                    failure(urlRequest, operation.response, error);
                }

                if (operation == strongSelf.af_imageRequestOperation){
                        strongSelf.af_imageRequestOperation = nil;
                }
            }
        }];

使用 

[[[strongSelf class]sharedImageCache] cacheImage:responseObjectforRequest:urlRequest];

本質上 就是使用NSCache 在memory上進行快取。

看看 SDWebImage 的圖片快取:

在 UIImageView+WebCache.m

- (void)sd_setImageWithURL:(NSURL *)url {
    [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:nil];
}

- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder {
    [self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:nil];
}

/**
 * Andrew Zhang add
 */
- (void)sd_setVedioImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder{
    [self sd_setVedioImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:nil];
}

- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options {
    [self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:nil];
}

- (void)sd_setImageWithURL:(NSURL *)url completed:(SDWebImageCompletionBlock)completedBlock {
    [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:completedBlock];
}

- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletionBlock)completedBlock {
    [self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:completedBlock];
}

- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletionBlock)completedBlock {
    [self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:completedBlock];
}

- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock {
    [self sd_cancelCurrentImageLoad];
    objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

    if (!(options & SDWebImageDelayPlaceholder)) {
        dispatch_main_async_safe(^{
            self.image = placeholder;
        });
    }
    
    if (url) {
        __weak UIImageView *wself = self;
       <span style="color:#ff0000;"> id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager downloadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) </span>{
            if (!wself) return;
            dispatch_main_sync_safe(^{
                if (!wself) return;
                if (image) {
                    wself.image = image;
                    [wself setNeedsLayout];
                } else {
                    if ((options & SDWebImageDelayPlaceholder)) {
                        wself.image = placeholder;
                        [wself setNeedsLayout];
                    }
                }
                if (completedBlock && finished) {
                    completedBlock(image, error, cacheType, url);
                }
            });
        }];
        [self sd_setImageLoadOperation:operation forKey:@"UIImageViewImageLoad"];
    } else {
        dispatch_main_async_safe(^{
            NSError *error = [NSError errorWithDomain:@"SDWebImageErrorDomain" code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
            if (completedBlock) {
                completedBlock(nil, error, SDImageCacheTypeNone, url);
            }
        });
    }
}

使用了  id <SDWebImageOperation> operation = [SDWebImageManager.sharedManagerdownloadImageWithURL:urloptions:optionsprogress:progressBlockcompleted:^(UIImage *image,NSError *error,SDImageCacheType cacheType,BOOL finished,NSURL *imageURL) 來進行圖片下載,接著我們去看看 這個方法怎麼實現的。
- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url
                                         options:(SDWebImageOptions)options
                                        progress:(SDWebImageDownloaderProgressBlock)progressBlock
                                       completed:(SDWebImageCompletionWithFinishedBlock)completedBlock {
    // Invoking this method without a completedBlock is pointless
    NSAssert(completedBlock != nil, @"If you mean to prefetch the image, use -[SDWebImagePrefetcher prefetchURLs] instead");

    // Very common mistake is to send the URL using NSString object instead of NSURL. For some strange reason, XCode won't
    // throw any warning for this type mismatch. Here we failsafe this error by allowing URLs to be passed as NSString.
    if ([url isKindOfClass:NSString.class]) {
        url = [NSURL URLWithString:(NSString *)url];
    }

    // Prevents app crashing on argument type error like sending NSNull instead of NSURL
    if (![url isKindOfClass:NSURL.class]) {
        url = nil;
    }

    __block SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new];
    __weak SDWebImageCombinedOperation *weakOperation = operation;

    BOOL isFailedUrl = NO;
    @synchronized (self.failedURLs) {
        isFailedUrl = [self.failedURLs containsObject:url];
    }

    if (!url || (!(options & SDWebImageRetryFailed) && isFailedUrl)) {
        dispatch_main_sync_safe(^{
            NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil];
            completedBlock(nil, error, SDImageCacheTypeNone, YES, url);
        });
        return operation;
    }

    @synchronized (self.runningOperations) {
        [self.runningOperations addObject:operation];
    }
    NSString *key = [self cacheKeyForURL:url];

    <span style="color:#ff0000;">operation.cacheOperation = [self.imageCache queryDiskCacheForKey:key done:^(UIImage *image, SDImageCacheType cacheType) {</span>
        if (operation.isCancelled) {
            @synchronized (self.runningOperations) {
                [self.runningOperations removeObject:operation];
            }

            return;
        }

        if ((!image || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url])) {
            if (image && options & SDWebImageRefreshCached) {
                dispatch_main_sync_safe(^{
                    // If image was found in the cache bug SDWebImageRefreshCached is provided, notify about the cached image
                    // AND try to re-download it in order to let a chance to NSURLCache to refresh it from server.
                    completedBlock(image, nil, cacheType, YES, url);
                });
            }

            // download if no image or requested to refresh anyway, and download allowed by delegate
            SDWebImageDownloaderOptions downloaderOptions = 0;
            if (options & SDWebImageLowPriority) downloaderOptions |= SDWebImageDownloaderLowPriority;
            if (options & SDWebImageProgressiveDownload) downloaderOptions |= SDWebImageDownloaderProgressiveDownload;
            if (options & SDWebImageRefreshCached) downloaderOptions |= SDWebImageDownloaderUseNSURLCache;
            if (options & SDWebImageContinueInBackground) downloaderOptions |= SDWebImageDownloaderContinueInBackground;
            if (options & SDWebImageHandleCookies) downloaderOptions |= SDWebImageDownloaderHandleCookies;
            if (options & SDWebImageAllowInvalidSSLCertificates) downloaderOptions |= SDWebImageDownloaderAllowInvalidSSLCertificates;
            if (options & SDWebImageHighPriority) downloaderOptions |= SDWebImageDownloaderHighPriority;
            if (image && options & SDWebImageRefreshCached) {
                // force progressive off if image already cached but forced refreshing
                downloaderOptions &= ~SDWebImageDownloaderProgressiveDownload;
                // ignore image read from NSURLCache if image if cached but force refreshing
                downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse;
            }
            id <SDWebImageOperation> subOperation = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *data, NSError *error, BOOL finished) {
                if (weakOperation.isCancelled) {
                    // Do nothing if the operation was cancelled
                    // See #699 for more details
                    // if we would call the completedBlock, there could be a race condition between this block and another completedBlock for the same object, so if this one is called second, we will overwrite the new data
                }
                else if (error) {
                    dispatch_main_sync_safe(^{
                        if (!weakOperation.isCancelled) {
                            completedBlock(nil, error, SDImageCacheTypeNone, finished, url);
                        }
                    });

                    if (error.code != NSURLErrorNotConnectedToInternet && error.code != NSURLErrorCancelled && error.code != NSURLErrorTimedOut) {
                        @synchronized (self.failedURLs) {
                            if (![self.failedURLs containsObject:url]) {
                                [self.failedURLs addObject:url];
                            }
                        }
                    }
                }
                else {
                    BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly);

                    if (options & SDWebImageRefreshCached && image && !downloadedImage) {
                        // Image refresh hit the NSURLCache cache, do not call the completion block
                    }
                    else if (downloadedImage && (!downloadedImage.images || (options & SDWebImageTransformAnimatedImage)) && [self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)]) {
                        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
                            UIImage *transformedImage = [self.delegate imageManager:self transformDownloadedImage:downloadedImage withURL:url];

                            if (transformedImage && finished) {
                                BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage];
                               <span style="color:#ff0000;"> [self.imageCache storeImage:transformedImage recalculateFromImage:imageWasTransformed imageData:data forKey:key toDisk:cacheOnDisk];</span>
                            }

                            dispatch_main_sync_safe(^{
                                if (!weakOperation.isCancelled) {
                                    completedBlock(transformedImage, nil, SDImageCacheTypeNone, finished, url);
                                }
                            });
                        });
                    }
                    else {
                        if (downloadedImage && finished) {
                            [self.imageCache storeImage:downloadedImage recalculateFromImage:NO imageData:data forKey:key toDisk:cacheOnDisk];
                        }

                        dispatch_main_sync_safe(^{
                            if (!weakOperation.isCancelled) {
                                completedBlock(downloadedImage, nil, SDImageCacheTypeNone, finished, url);
                            }
                        });
                    }
                }

                if (finished) {
                    @synchronized (self.runningOperations) {
                        [self.runningOperations removeObject:operation];
                    }
                }
            }];
            operation.cancelBlock = ^{
                [subOperation cancel];
                
                @synchronized (self.runningOperations) {
                    [self.runningOperations removeObject:weakOperation];
                }
            };
        }
       <span style="color:#ff0000;"> else if (image) {
            dispatch_main_sync_safe(^{
                if (!weakOperation.isCancelled) {
                    completedBlock(image, nil, cacheType, YES, url);
                }
            });
            @synchronized (self.runningOperations) {
                [self.runningOperations removeObject:operation];
            }
        }</span>
        else {
            // Image not in cache and download disallowed by delegate
            dispatch_main_sync_safe(^{
                if (!weakOperation.isCancelled) {
                    completedBlock(nil, nil, SDImageCacheTypeNone, YES, url);
                }
            });
            @synchronized (self.runningOperations) {
                [self.runningOperations removeObject:operation];
            }
        }
    }];

    return operation;
}

在 這個實現方法中,可以看到,首先使用:

operation.cacheOperation = [self.imageCachequeryDiskCacheForKey:key done:^(UIImage *image,SDImageCacheType cacheType)  去查詢本地磁碟上和memory上是否存在圖片。到內部去看看:

- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock {
    if (!doneBlock) {
        return nil;
    }

    if (!key) {
        doneBlock(nil, SDImageCacheTypeNone);
        return nil;
    }

    <span style="color:#ff0000;">// First check the in-memory cache...
    UIImage *image = [self imageFromMemoryCacheForKey:key];</span>
    if (image) {
        doneBlock(image, SDImageCacheTypeMemory);
        return nil;
    }

    NSOperation *operation = [NSOperation new];
    dispatch_async(self.ioQueue, ^{
        if (operation.isCancelled) {
            return;
        }

        @autoreleasepool {
            <span style="color:#ff0000;">UIImage *diskImage = [self diskImageForKey:key];
            if (diskImage) {
                CGFloat cost = diskImage.size.height * diskImage.size.width * diskImage.scale * diskImage.scale;
                [self.memCache setObject:diskImage forKey:key cost:cost];
            }
</span>
            dispatch_async(dispatch_get_main_queue(), ^{
                doneBlock(diskImage, SDImageCacheTypeDisk);
            });
        }
    });

    return operation;
}

這個方法是先去 查詢memory上是否命中,如果命中 直接返回圖片,如果沒有,則再去查詢本地磁碟是否有快取。

好了,再看看,如果memory 和 本地磁碟上都沒有圖片快取 SDWebImage是怎麼做的?

 if ((!image || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url])) {
            if (image && options & SDWebImageRefreshCached) {
                dispatch_main_sync_safe(^{
                    // If image was found in the cache bug SDWebImageRefreshCached is provided, notify about the cached image
                    // AND try to re-download it in order to let a chance to NSURLCache to refresh it from server.
                    completedBlock(image, nil, cacheType, YES, url);
                });
            }

            // download if no image or requested to refresh anyway, and download allowed by delegate
            SDWebImageDownloaderOptions downloaderOptions = 0;
            if (options & SDWebImageLowPriority) downloaderOptions |= SDWebImageDownloaderLowPriority;
            if (options & SDWebImageProgressiveDownload) downloaderOptions |= SDWebImageDownloaderProgressiveDownload;
            if (options & SDWebImageRefreshCached) downloaderOptions |= SDWebImageDownloaderUseNSURLCache;
            if (options & SDWebImageContinueInBackground) downloaderOptions |= SDWebImageDownloaderContinueInBackground;
            if (options & SDWebImageHandleCookies) downloaderOptions |= SDWebImageDownloaderHandleCookies;
            if (options & SDWebImageAllowInvalidSSLCertificates) downloaderOptions |= SDWebImageDownloaderAllowInvalidSSLCertificates;
            if (options & SDWebImageHighPriority) downloaderOptions |= SDWebImageDownloaderHighPriority;
            if (image && options & SDWebImageRefreshCached) {
                // force progressive off if image already cached but forced refreshing
                downloaderOptions &= ~SDWebImageDownloaderProgressiveDownload;
                // ignore image read from NSURLCache if image if cached but force refreshing
                downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse;
            }
            <span style="color:#ff0000;">id <SDWebImageOperation> subOperation = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *data, NSError *error, BOOL finished)</span> {
                if (weakOperation.isCancelled) {
                    // Do nothing if the operation was cancelled
                    // See #699 for more details
                    // if we would call the completedBlock, there could be a race condition between this block and another completedBlock for the same object, so if this one is called second, we will overwrite the new data
                }
                else if (error) {
                    dispatch_main_sync_safe(^{
                        if (!weakOperation.isCancelled) {
                            completedBlock(nil, error, SDImageCacheTypeNone, finished, url);
                        }
                    });

                    if (error.code != NSURLErrorNotConnectedToInternet && error.code != NSURLErrorCancelled && error.code != NSURLErrorTimedOut) {
                        @synchronized (self.failedURLs) {
                            if (![self.failedURLs containsObject:url]) {
                                [self.failedURLs addObject:url];
                            }
                        }
                    }
                }
                else {
                    BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly);

                    if (options & SDWebImageRefreshCached && image && !downloadedImage) {
                        // Image refresh hit the NSURLCache cache, do not call the completion block
                    }
                    else if (downloadedImage && (!downloadedImage.images || (options & SDWebImageTransformAnimatedImage)) && [self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)]) {
                        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
                            UIImage *transformedImage = [self.delegate imageManager:self transformDownloadedImage:downloadedImage withURL:url];

                           <span style="color:#ff0000;"> if (transformedImage && finished) {
                                BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage];
                                [self.imageCache storeImage:transformedImage recalculateFromImage:imageWasTransformed imageData:data forKey:key toDisk:cacheOnDisk];
                            }</span>

                            dispatch_main_sync_safe(^{
                                if (!weakOperation.isCancelled) {
                                    completedBlock(transformedImage, nil, SDImageCacheTypeNone, finished, url);
                                }
                            });
                        });
                    }
                    else {
                        if (downloadedImage && finished) {
                            [self.imageCache storeImage:downloadedImage recalculateFromImage:NO imageData:data forKey:key toDisk:cacheOnDisk];
                        }

                        dispatch_main_sync_safe(^{
                            if (!weakOperation.isCancelled) {
                                completedBlock(downloadedImage, nil, SDImageCacheTypeNone, finished, url);
                            }
                        });
                    }
                }

                if (finished) {
                    @synchronized (self.runningOperations) {
                        [self.runningOperations removeObject:operation];
                    }
                }
            }];

使用 

id <SDWebImageOperation> subOperation = [self.imageDownloaderdownloadImageWithURL:url options:downloaderOptionsprogress:progressBlock completed:^(UIImage *downloadedImage,NSData *data, NSError *error,BOOL finished) 子操作,來下載圖片,到  SDWebImageDownloader.h  中看看具體的原始碼:

- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url options:(SDWebImageDownloaderOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageDownloaderCompletedBlock)completedBlock {
    __block SDWebImageDownloaderOperation *operation;
    __weak SDWebImageDownloader *wself = self;

    [self addProgressCallback:progressBlock andCompletedBlock:completedBlock forURL:url createCallback:^{
        NSTimeInterval timeoutInterval = wself.downloadTimeout;
        if (timeoutInterval == 0.0) {
            timeoutInterval = 15.0;
        }

        // In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests if told otherwise
       <span style="color:#ff0000;"> NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:(options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData) timeoutInterval:timeoutInterval];</span>
        request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies);
        request.HTTPShouldUsePipelining = YES;
        if (wself.headersFilter) {
            request.allHTTPHeaderFields = wself.headersFilter(url, [wself.HTTPHeaders copy]);
        }
        else {
            request.allHTTPHeaderFields = wself.HTTPHeaders;
        }
        <span style="color:#ff0000;">operation = [[wself.operationClass alloc] initWithRequest:request
                                                          options:options
                                                         progress:^(NSInteger receivedSize, NSInteger expectedSize)</span> {
                                                             SDWebImageDownloader *sself = wself;
                                                             if (!sself) return;
                                                             NSArray *callbacksForURL = [sself callbacksForURL:url];
                                                             for (NSDictionary *callbacks in callbacksForURL) {
                                                                 SDWebImageDownloaderProgressBlock callback = callbacks[kProgressCallbackKey];
                                                                 if (callback) callback(receivedSize, expectedSize);
                                                             }
                                                         }
                                                        completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
                                                            SDWebImageDownloader *sself = wself;
                                                            if (!sself) return;
                                                            NSArray *callbacksForURL = [sself callbacksForURL:url];
                                                            if (finished) {
                                                                [sself removeCallbacksForURL:url];
                                                            }
                                                            for (NSDictionary *callbacks in callbacksForURL) {
                                                                SDWebImageDownloaderCompletedBlock callback = callbacks[kCompletedCallbackKey];
                                                                if (callback) callback(image, data, error, finished);
                                                            }
                                                        }
                                                        cancelled:^{
                                                            SDWebImageDownloader *sself = wself;
                                                            if (!sself) return;
                                                            [sself removeCallbacksForURL:url];
                                                        }];
        
        if (wself.username && wself.password) {
            operation.credential = [NSURLCredential credentialWithUser:wself.username password:wself.password persistence:NSURLCredentialPersistenceForSession];
        }
        
        if (options & SDWebImageDownloaderHighPriority) {
            operation.queuePriority = NSOperationQueuePriorityHigh;
        } else if (options & SDWebImageDownloaderLowPriority) {
            operation.queuePriority = NSOperationQueuePriorityLow;
        }

        [wself.downloadQueue addOperation:operation];
        if (wself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) {
            // Emulate LIFO execution order by systematically adding new operations as last operation's dependency
            [wself.lastAddedOperation addDependency:operation];
            wself.lastAddedOperation = operation;
        }
    }];

    return operation;
}

注意:這句  NSMutableURLRequest *request = [[NSMutableURLRequestalloc] initWithURL:urlcachePolicy:(options & SDWebImageDownloaderUseNSURLCache ?NSURLRequestUseProtocolCachePolicy :NSURLRequestReloadIgnoringLocalCacheData)timeoutInterval:timeoutInterval];

可見預設使用了帶快取的網路請求。


然後 將 request使用 

operation = [[wself.operationClassalloc] initWithRequest:request

options:options

progress:^(NSInteger receivedSize,NSInteger expectedSize)

進行請求。

SDWebImageDownloaderOperation.m 中 來看看 具體是怎麼請求的?

- (id)initWithRequest:(NSURLRequest *)request
              options:(SDWebImageDownloaderOptions)options
             progress:(SDWebImageDownloaderProgressBlock)progressBlock
            completed:(SDWebImageDownloaderCompletedBlock)completedBlock
            cancelled:(SDWebImageNoParamsBlock)cancelBlock {
    if ((self = [super init])) {
        _request = request;
        _shouldUseCredentialStorage = YES;
        _options = options;
        _progressBlock = [progressBlock copy];
        _completedBlock = [completedBlock copy];
        _cancelBlock = [cancelBlock copy];
        _executing = NO;
        _finished = NO;
        _expectedSize = 0;
        responseFromCached = YES; // Initially wrong until `connection:willCacheResponse:` is called or not called
    }
    return self;
}

#pragma mark NSURLConnection (delegate)

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    
    //'304 Not Modified' is an exceptional one
    if ((![response respondsToSelector:@selector(statusCode)] || [((NSHTTPURLResponse *)response) statusCode] < 400) && [((NSHTTPURLResponse *)response) statusCode] != 304) {
        NSInteger expected = response.expectedContentLength > 0 ? (NSInteger)response.expectedContentLength : 0;
        self.expectedSize = expected;
        if (self.progressBlock) {
            self.progressBlock(0, expected);
        }

        self.imageData = [[NSMutableData alloc] initWithCapacity:expected];
    }
    else {
        NSUInteger code = [((NSHTTPURLResponse *)response) statusCode];
        
        //This is the case when server returns '304 Not Modified'. It means that remote image is not changed.
        //In case of 304 we need just cancel the operation and return cached image from the cache.
        if (code == 304) {
            [self cancelInternal];
        } else {
            [self.connection cancel];
        }
        dispatch_async(dispatch_get_main_queue(), ^{
            [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:nil];
        });

        if (self.completedBlock) {
            self.completedBlock(nil, nil, [NSError errorWithDomain:NSURLErrorDomain code:[((NSHTTPURLResponse *)response) statusCode] userInfo:nil], YES);
        }
        CFRunLoopStop(CFRunLoopGetCurrent());
        [self done];
    }
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [self.imageData appendData:data];

    if ((self.options & SDWebImageDownloaderProgressiveDownload) && self.expectedSize > 0 && self.completedBlock) {
        // The following code is from http://www.cocoaintheshell.com/2011/05/progressive-images-download-imageio/
        // Thanks to the author @Nyx0uf

        // Get the total bytes downloaded
        const NSInteger totalSize = self.imageData.length;

        // Update the data source, we must pass ALL the data, not just the new bytes
        CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)self.imageData, NULL);

        if (width + height == 0) {
            CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL);
            if (properties) {
                NSInteger orientationValue = -1;
                CFTypeRef val = CFDictionaryGetValue(properties, kCGImagePropertyPixelHeight);
                if (val) CFNumberGetValue(val, kCFNumberLongType, &height);
                val = CFDictionaryGetValue(properties, kCGImagePropertyPixelWidth);
                if (val) CFNumberGetValue(val, kCFNumberLongType, &width);
                val = CFDictionaryGetValue(properties, kCGImagePropertyOrientation);
                if (val) CFNumberGetValue(val, kCFNumberNSIntegerType, &orientationValue);
                CFRelease(properties);

                // When we draw to Core Graphics, we lose orientation information,
                // which means the image below born of initWithCGIImage will be
                // oriented incorrectly sometimes. (Unlike the image born of initWithData
                // in connectionDidFinishLoading.) So save it here and pass it on later.
                orientation = [[self class] orientationFromPropertyValue:(orientationValue == -1 ? 1 : orientationValue)];
            }

        }

        if (width + height > 0 && totalSize < self.expectedSize) {
            // Create the image
            CGImageRef partialImageRef = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL);

#ifdef TARGET_OS_IPHONE
            // Workaround for iOS anamorphic image
            if (partialImageRef) {
                const size_t partialHeight = CGImageGetHeight(partialImageRef);
                CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
                CGContextRef bmContext = CGBitmapContextCreate(NULL, width, height, 8, width * 4, colorSpace, kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst);
                CGColorSpaceRelease(colorSpace);
                if (bmContext) {
                    CGContextDrawImage(bmContext, (CGRect){.origin.x = 0.0f, .origin.y = 0.0f, .size.width = width, .size.height = partialHeight}, partialImageRef);
                    CGImageRelease(partialImageRef);
                    partialImageRef = CGBitmapContextCreateImage(bmContext);
                    CGContextRelease(bmContext);
                }
                else {
                    CGImageRelease(partialImageRef);
                    partialImageRef = nil;
                }
            }
#endif

            if (partialImageRef) {
                UIImage *image = [UIImage imageWithCGImage:partialImageRef scale:1 orientation:orientation];
                NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:self.request.URL];
                UIImage *scaledImage = [self scaledImageForKey:key image:image];
                image = [UIImage decodedImageWithImage:scaledImage];
                CGImageRelease(partialImageRef);
                dispatch_main_sync_safe(^{
                    if (self.completedBlock) {
                        self.completedBlock(image, nil, nil, NO);
                    }
                });
            }
        }

        CFRelease(imageSource);
    }

    if (self.progressBlock) {
        self.progressBlock(self.imageData.length, self.expectedSize);
    }
}

+ (UIImageOrientation)orientationFromPropertyValue:(NSInteger)value {
    switch (value) {
        case 1:
            return UIImageOrientationUp;
        case 3:
            return UIImageOrientationDown;
        case 8:
            return UIImageOrientationLeft;
        case 6:
            return UIImageOrientationRight;
        case 2:
            return UIImageOrientationUpMirrored;
        case 4:
            return UIImageOrientationDownMirrored;
        case 5:
            return UIImageOrientationLeftMirrored;
        case 7:
            return UIImageOrientationRightMirrored;
        default:
            return UIImageOrientationUp;
    }
}

- (UIImage *)scaledImageForKey:(NSString *)key image:(UIImage *)image {
    return SDScaledImageForKey(key, image);
}

- (void)connectionDidFinishLoading:(NSURLConnection *)aConnection {
    SDWebImageDownloaderCompletedBlock completionBlock = self.completedBlock;
    @synchronized(self) {
        CFRunLoopStop(CFRunLoopGetCurrent());
        self.thread = nil;
        self.connection = nil;
        dispatch_async(dispatch_get_main_queue(), ^{
            [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:nil];
        });
    }
    
    <span style="color:#ff0000;">if (![[NSURLCache sharedURLCache] cachedResponseForRequest:_request]) {
        responseFromCached = NO;
    }</span>
    
    if (completionBlock) {
        if (self.options & SDWebImageDownloaderIgnoreCachedResponse && responseFromCached) {
            completionBlock(nil, nil, nil, YES);
        }
        else {
            UIImage *image = [UIImage sd_imageWithData:self.imageData];
            NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:self.request.URL];
            image = [self scaledImageForKey:key image:image];
            
            // Do not force decoding animated GIFs
            if (!image.images) {
                image = [UIImage decodedImageWithImage:image];
            }
            if (CGSizeEqualToSize(image.size, CGSizeZero)) {
                completionBlock(nil, nil, [NSError errorWithDomain:@"SDWebImageErrorDomain" code:0 userInfo:@{NSLocalizedDescriptionKey : @"Downloaded image has 0 pixels"}], YES);
            }
            else {
                completionBlock(image, self.imageData, nil, YES);
            }
        }
    }
    self.completionBlock = nil;
    [self done];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    @synchronized(self) {
        CFRunLoopStop(CFRunLoopGetCurrent());
        self.thread = nil;
        self.connection = nil;
        dispatch_async(dispatch_get_main_queue(), ^{
            [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:nil];
        });
    }

    if (self.completedBlock) {
        self.completedBlock(nil, nil, error, YES);
    }
    self.completionBlock = nil;
    [self done];
}

- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse {
    responseFromCached = NO; // If this method is called, it means the response wasn't read from cache
    if (self.request.cachePolicy == NSURLRequestReloadIgnoringLocalCacheData) {
        // Prevents caching of responses
        return nil;
    }
    else {
        return cachedResponse;
    }
}

依然使用最基本的  NSURLConnection 進行網路請求。但是注意 在 

- (void)connectionDidFinishLoading:(NSURLConnection *)aConnection 方法中:

if (![[NSURLCachesharedURLCache] cachedResponseForRequest:_request]) {

responseFromCached =NO;

    }

並且:

- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse {
    responseFromCached = NO; // If this method is called, it means the response wasn't read from cache
    if (self.request.cachePolicy == NSURLRequestReloadIgnoringLocalCacheData) {
        // Prevents caching of responses
        return nil;
    }
    else {
        return cachedResponse;
    }
}

可以看到 SDWebImage 使用了 NSURLCache 進行網路請求快取。

回到最開始 ,假設圖片下載完成了,SDWebImage 又做了什麼事情呢?

 if (transformedImage && finished) {
                                BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage];
                                [self.imageCache storeImage:transformedImage recalculateFromImage:imageWasTransformed imageData:data forKey:key toDisk:cacheOnDisk];
                            }

使用 
 [self.imageCache storeImage:transformedImage recalculateFromImage:imageWasTransformed imageData:data forKey:key toDisk:cacheOnDisk];
- (void)storeImage:(UIImage *)image recalculateFromImage:(BOOL)recalculate imageData:(NSData *)imageData forKey:(NSString *)key toDisk:(BOOL)toDisk {
    if (!image || !key) {
        return;
    }
    
   <span style="color:#ff0000;"> [self.memCache setObject:image forKey:key cost:image.size.height * image.size.width * image.scale * image.scale];</span>

    if (toDisk) {
        dispatch_async(self.ioQueue, ^{
            NSData *data = imageData;

            if (image && (recalculate || !data)) {
#if TARGET_OS_IPHONE
                // We need to determine if the image is a PNG or a JPEG
                // PNGs are easier to detect because they have a unique signature (http://www.w3.org/TR/PNG-Structure.html)
                // The first eight bytes of a PNG file always contain the following (decimal) values:
                // 137 80 78 71 13 10 26 10

                // We assume the image is PNG, in case the imageData is nil (i.e. if trying to save a UIImage directly),
                // we will consider it PNG to avoid loosing the transparency
                BOOL imageIsPng = YES;

                // But if we have an image data, we will look at the preffix
                if ([imageData length] >= [kPNGSignatureData length]) {
                    imageIsPng = ImageDataHasPNGPreffix(imageData);
                }

                if (imageIsPng) {
                    data = UIImagePNGRepresentation(image);
                }
                else {
                    data = UIImageJPEGRepresentation(image, (CGFloat)1.0);
                }
#else
                data = [NSBitmapImageRep representationOfImageRepsInArray:image.representations usingType: NSJPEGFileType properties:nil];
#endif
            }

            if (data) {
              <span style="color:#ff0000;">  if (![_fileManager fileExistsAtPath:_diskCachePath]) {
                    [_fileManager createDirectoryAtPath:_diskCachePath withIntermediateDirectories:YES attributes:nil error:NULL];
                }

                [_fileManager createFileAtPath:[self defaultCachePathForKey:key] contents:data attributes:nil];</span>
            }
        });
    }
}

可見,先使用NSCache 快取到memory上,然後 使用  NSFileManager  將圖片資料(注意:依舊是 UIImage型別,而不是NSData型別)寫入到本地磁碟路徑。

綜上所述:

得出如下結論:

     UIImageView+AFNetworking 的圖片快取,並沒有本地磁碟快取,且網路請求圖片中並未使用NSURLCache,來進行網路請求快取。只是使用了NSCache 在memory上進行快取。由於只是在memory上快取了,所以當app從記憶體中退出後,重新開啟app,每次都會重新對圖片進行網路請求。

     SDWebImage 的圖片快取,不僅使用了NSCache 在memory上進行快取,而且還使用了NSFileManager在本地磁碟上進行快取。同時在 使用NSURLConnection 時,使用了NSURLCache進行網路請求快取(這樣有什麼好處?1: 當一次圖片請求因某種原因中途斷開,下次請求時,可以直接從NSURLCache獲取之前請求的一部分資料,接著請求餘下的資料即可。2:當快取策略不同時,比如:每次都請求,不快取。那麼這時候,只要第一次進行了NSURLConnection,以後都可以直接從NSURLCache 獲取資料。

由此,可知:SDWebImage 在圖片快取上比UIImageView+AFNetworking 有著明顯的優勢,如果對於一款app在圖片上的效能優化,推薦使用 SDWebImage 來替代其他框架。這樣可以節省使用者很多的流量。


-----------下面轉載幾篇關於  NSURLCache的文章-----------

1:NSURLCache 為您的應用的 URL 請求提供了記憶體中以及磁碟上的綜合快取機制。 作為基礎類庫 URL 載入系統 的一部分,任何通過 NSURLConnection 載入的請求都將被 NSURLCache 處理。

網路快取減少了需要向伺服器傳送請求的次數,同時也提升了離線或在低速網路中使用應用的體驗。

當一個請求完成下載來自伺服器的迴應,一個快取的迴應將在本地儲存。下一次同一個請求再發起時,本地儲存的迴應就會馬上返回,不需要連線伺服器。NSURLCache 會 自動 且 透明 地返回迴應。

為了好好利用 NSURLCache,你需要初始化並設定一個共享的 URL 快取。在 iOS 中這項工作需要在 -application:didFinishLaunchingWithOptions: 完成,而 Mac OS X 中是在 –applicationDidFinishLaunching:

- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  NSURLCache *URLCache = [[NSURLCache alloc] initWithMemoryCapacity:4 * 1024 * 1024
                                                       diskCapacity:20 * 1024 * 1024
                                                           diskPath:nil];
  [NSURLCache setSharedURLCache:URLCache];
}

快取策略由請求(客戶端)和迴應(服務端)分別指定。理解這些策略以及它們如何相互影響,是為您的應用程式找到最佳行為的關鍵。

NSURLRequestCachePolicy

NSURLRequest 有個 cachePolicy 屬性,它根據以下常量指定了請求的快取行為:

  • NSURLRequestUseProtocolCachePolicy: 對特定的 URL 請求使用網路協議中實現的快取邏輯。這是預設的策略。
  • NSURLRequestReloadIgnoringLocalCacheData:資料需要從原始地址載入。不使用現有快取。
  • NSURLRequestReloadIgnoringLocalAndRemoteCacheData:不僅忽略本地快取,同時也忽略代理伺服器或其他中間介質目前已有的、協議允許的快取。
  • NSURLRequestReturnCacheDataElseLoad:無論快取是否過期,先使用本地快取資料。如果快取中沒有請求所對應的資料,那麼從原始地址載入資料。
  • NSURLRequestReturnCacheDataDontLoad:無論快取是否過期,先使用本地快取資料。如果快取中沒有請求所對應的資料,那麼放棄從原始地址載入資料,請求視為失敗(即:“離線”模式)。
  • NSURLRequestReloadRevalidatingCacheData:從原始地址確認快取資料的合法性後,快取資料就可以使用,否則從原始地址載入。

你並不會驚奇於這些值不被透徹理解且經常搞混淆。

NSURLRequestReloadIgnoringLocalAndRemoteCacheData 和 NSURLRequestReloadRevalidatingCacheData 根本沒有實現Link to Radar)更加加深了混亂程度!

關於NSURLRequestCachePolicy,以下才是你 實際 需要了解的東西:

常量 意義
UseProtocolCachePolicy

相關推薦

AFNetWorking SDWebImage 圖片快取對比

文章 篇幅 較長,其中有很多對比原始碼的地方。如果沒有耐心看分析,可以直接看 最後的結論。 首先 提下: NSURLCache   和 NSCache。 NSURLCache: 可以在memory 和 disk 上快取。但是在memory上快取時,沒有自動清理

OpenCVFFMpeg圖片轉換對比

最近一直在處理圖片,從H264解碼後得到的圖片是YUV圖片,而且很多都是NV12的,不是YUV420P(它們的差別是NV12格式為YYY...Y UV UV UV ... UV,而420P格式為 YYY...YY UUU..U VVV...V),一張1920x1080的圖片大小為3.1M,為了節省空間,我需要

AFNetworkingYTKNetwork的快取策略

Untold numbers of developers have hacked together an awkward, fragile system for network caching functionality, all because they weren’t aware that NSUR

超讚的 SDWebImage 框架( AF提供的圖片快取比較 )

       SDWebImage 是一個超級牛逼的開源框架。我們 如果只滿足於公開的api來使用它,那麼你可能不會對這個開源框架的作者佩服,也就不知道這個框架是迄今為止,在ios中來說,快取時做的最好的一個(沒有之一)。 記得以前早些時候去百度面試的時候,那時的技術大牛問

IOS網路圖片快取SDWebImage

載入網路圖片可以說是網路應用中必備的。如果單純的去下載圖片,而不去做多執行緒、快取等技術去優化,載入圖片時的效果與使用者體驗就會很差。 處理網路圖片快取步驟: 1、根據圖片URL查詢記憶體是否有這張圖片,有則返回圖片,沒有則進入下一步。 2、查詢本地磁碟儲存是否有這張圖片,有則返回圖片,

64.ImageLoader原始碼分析-磁碟命名圖片快取演算法

一. 前言 ImageLoader的圖片快取分成磁碟和記憶體兩種,這裡分析一下磁碟快取以及圖片檔名演算法的實現 預設是不儲存在磁碟上的,需要手動開啟開關 如下 DisplayImageOptions options = new DisplayImageOptions.Builder()

圖片快取框架Picasso的學習使用

搞Android的都知道對圖片的下載和快取處理非常的麻煩至極,動不動就發生OOM之類的情況。特別是在Listview,GridView,ViewPage等控制元件裡面。至此介紹Picasso圖片快取框架使用。相對ImageLoad等框架更為方便快速開發者使用。介紹下Picasso; Pica

ListViewRecyclerView的快取機制的對比

簡單瞭解下快取的基本原理 1)在初始化onLayout過程中,都有一個 mAttachedxxx的集合,臨時存在即將顯示的第一屏的view,在最後一次onLayout結束之後,會從將該mAttachedxxx裡面的view渲染到第一屏頁面上。 2)當向上滑動過程中

Android 三大圖片快取原理、特性對比

轉至:Android 三大圖片快取原理、特性對比 一. 四大圖片快取基本資訊 Universal ImageLoader 是很早開源的圖片快取,在早期被很多應用使用。 Picasso 是 Square 開源的專案,且他的主導者是 JakeWharton,

android上的一個網路介面圖片快取框架enif

1.底層網路介面採用apache的httpclient連線池框架; 2.圖片快取採用基於LRU的演算法; 3.網路介面採用監聽者模式; 4.包含圖片的OOM處理(及時回收處理技術的應用);   圖片核心處理類:CacheView.java [java] v

android--------Universal-Image-Loader圖片載入框架結合LruCache快取圖片

本部落格包含包含Android-Universal-Image-Loader 網路圖片載入框架實現圖片載入和結合universal-image-loader與LruCache來自定義快取圖片,可以設定快取與不快取。 Android-Universal-Image-Load

Android 四大大圖片快取(Imageloader,Picasso,Glide,Fresco)原理、特性對比

四大圖片快取基本資訊 Universal ImageLoader 是很早開源的圖片快取,在早期被很多應用使用。 Picasso 是 Square 開源的專案,且他的主導者是 JakeWharton,所以廣為人知。 Glide 是 Google 員工的開源專案,被一些

圖片快取:ImageCacheImageSdCache(一)

對於圖片資源來說,你不可能讓應用每次獲取的時候都重新到遠端去下載,這樣會浪費資源,但是你又不能讓所有圖片資源都放到記憶體中去(雖然這樣載入會比較快),因為圖片資源往往會佔用很大的記憶體空間,容易導致OOM。那麼如果下載下來的圖片儲存到SDCard中,下次直接從SDCard上去獲取呢?這也是一種做法,我看了

開源選型之Android三大圖片快取原理、特性對比

轉:http://www.csdn.net/article/2015-10-21/2825984?ref=myread 一. 四大圖片快取基本資訊 Universal ImageLoader 是很早開源的圖片快取,在早期被很多應用使用。 Picasso 是 Sq

清除SQL Server資料快取執行計劃快取,檢視執行計劃的各種方式對比

清除資料和執行計劃快取: DBCC DROPCLEANBUFFERSDBCC FREEPROCCACHE 開啟統計資料: SET STATISTICS IO ON SET STATISTICS TIME ON 開啟執行計劃: SET SHOWPLAN_TEXT ONSET S

【LruCacheDiskLruCache結合】圖片快取機制

本文是對網路上幾篇文章的綜合,   第一部分,使用LruCache和DiskLruCache: 新建一個Android專案,起名叫PhotoWallDemo,這裡我使用的是Android 4.0的API。然後新建一個libcore.io包,並將DiskLruCache.j

iOS開發swift版非同步載入網路圖片(帶快取預設圖片)

iOS開發之swift版非同步載入網路圖片     與SDWebImage非同步載入網路圖片的功能相似,只是程式碼比較簡單,功能沒有SD的完善與強大,支援預設新增圖片,支援本地快取。      非同步載入圖片的核心程式碼如下:  func setZYHWebImage(ur

Android 四大圖片快取原理,特性對比

Universal ImageLoader 是很早開源的圖片快取,在早期被很多應用使用。 Picasso 是 Square 開源的專案,且他的主導者是 JakeWharton,所以廣為人知。 Glide 是 Google 員工的開源專案,被一些 Goog

【iOS沉思錄】SDWebImage圖片二級快取非同步載入基本原理

關於SDWebImage SDWebImage是一個針對圖片載入的外掛庫,提供了一個支援快取的用於非同步載入圖片的下載工具,特別的為常用的UI元素:UIImageView,UIButton和MKAnnotationView提供了Category類別擴充

三大圖片快取對比

一、四大圖片快取庫基本資訊 ImageLoader                                                                Picasso