异步加载图片

####概述
自己实现一个异步加载图片的扩展,功能类似于SDWebImage

######GitHub地址
https://github.com/SureEdding/LightAsyncImageloader_SelfStudy

####文件架构
—-ImageDownloaderManager.h
—-ImageDownloaderManager.m
//图片下载的控制类,用来管理队列,后续扩展处

----ImageDownloaderOperation.h
----ImageDownloaderOperation.m
//实际下载图片的NSOperation子类,用于网络请求

----UIImageView+AsyncImage.h
----UIImageView+AsyncImage.m
//UIImageView的Category,实际使用中调用函数处

####主要思想

UIImageView调用加载图片的函数之后,category调用ImageDownloaderManager中的方法,ImageDownloaderManager先从本地缓存当中看是否存在缓存,如果击中,则直接返回图片。否则创建一个ImageDownloaderOperation,来获取网络图片,获取图片之后,更新本地缓存。

####Code讲解

#####UIImageView+AsyncImage.h

/**
 *    @author Sure Edding, 15-08-04 12:08:50
 *
 *    @brief  通过URL获取网络图片,图片未获取完成之前显示Placeholder中的图片,功能类似SDWebImage
 *
 *    @param url         网络获取图片链接
 *    @param placeHolder 获取数据未完成时候的占位图
 *
 *    @since 1.0
 */

- (void)imageWithURL:(nullable NSString *)url
    placeHolderImage:(nullable UIImage *)placeHolder
    progressBlock:(nullable progressBlock)progress
       completeBlock:(nullable void (^)(void))completeBlock;        

这是目前实现的一个基本的方法,作为每一个UIImageView都可以调用的一个实例方法

#####UIImageView+AsyncImage.m

- (void)imageWithURL:(nullable NSString *)url
    placeHolderImage:(nullable UIImage *)placeHolder
          progressBlock:(nullable progressBlock)progress
          completeBlock:(nullable void (^)(void))completeBlock
          {
if (url)
{
    __block UIImage *expectedImage;
    __weak __typeof(self)weakSelf = self;
    if (placeHolder)
    {
        dispatch_main_sync_safe(^{self.image = placeHolder;});
    }
    [[ImageDownloaderManager shareInstance] downloadImageWithURL:url
                                                    downloadType:(progress ? dBigImageDownload : dNormalDownload)
                                                   progressBlock:progress
                                                   completeBlock:^(UIImage *image) {
                                                       NSLog(@"CompleteBlock");
                                                       expectedImage = [image copy];
                                                       dispatch_main_sync_safe(^{
                                                           weakSelf.image = image;
                                                       });
                                                       [weakSelf setNeedsLayout];}
                                                    failureBlock:^(NSString *message) {
                                                        NSLog(@"faulure: %@", message);
                                                    }];}

    if (completeBlock) {
        completeBlock();
    }
}

这个方法在url成立的时候,会调用ImageDownloaderManager的实例方法,其中,ImageDownloaderManager是一个单例

#####ImageDownloaderManager.m

- (void)downloadImageWithURL:(NSString *)url
                downloadType:(DownloadPolicy)policy
               progressBlock:(progressBlock)progressBlock
               completeBlock:(completeBlock)completeBlock
                failureBlock:(failBlock)failureBlock
{
    NSURL *requestURL = [NSURL URLWithString:url];
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL: requestURL
                                                                cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                            timeoutInterval:10.f];

    [self queryDiskCacheForKey:url completeBlock:^(NSData *data, BOOL success, NSString *errorMessage) {
        if (success) {
            NSLog(@"CacheHit");
            completeBlock([UIImage imageWithData:data]);
        }
        return;
    }];
    ImageDownloaderOperation *operation = [[ImageDownloaderOperation alloc] initWithOperationId:url
                                                                                        request:request
                                                                                 downloadPolicy:policy
                                                                                  progressBlock:progressBlock
                                                                                  completeBlock:completeBlock
                                                                                     cacheBlock:^(NSData *cacheData) {
                                                                                         @synchronized(_imageCache) {
                                                                                             [_imageCache setObject:cacheData forKey:url];
                                                                                         }
                                                                                     } failureBlock:failureBlock];
    [_runningQueue addOperation:operation];
}

ImageDownloaderManager相当于一个管理者,管理着进程队列(NSOperation),每一个来自UIImageView的加载图片请求都会调用这个函数,然后在函数中先根据url初始化一个NSURLRequest,然后用这个request去初始化一个继承于NSOperation的子类ImageDownloaderOperation,然后在加入进程队列中,进程队列会帮你管理进程的执行。

- (instancetype)initWithOperationId:(nonnull    NSString *)operationId
                            request:(nonnull    NSURLRequest *)request
                     downloadPolicy:(           DownloadPolicy)policy
                      progressBlock:(nullable   progressBlock)progress
                      completeBlock:(nonnull    completeBlock)complete
                         cacheBlock:(nonnull    cacheBlock)cache
                       failureBlock:(nullable   failBlock)failure;
{
    if (self = [super init]) {
        _request = request;
        _operationId = operationId;
        _progressblock = progress;
        _completeblock = complete;
        _failblock  = failure;
        _cacheblock = cache;
        _downloadPolicy = policy;
        _session = [NSURLSession sessionWithConfiguration:    [NSURLSessionConfiguration defaultSessionConfiguration] delegate:self     delegateQueue:nil];
    }
    return self;
}

关键的网络获取的代码就在ImageDownloaderOperation当中实现

#####ImageDownloaderOperation.m

- (void)start
{
    if (dNormalDownload == _downloadPolicy)
    {
        NSURLSessionDataTask *task = [_session dataTaskWithRequest:_request completionHandler:
                                      ^(NSData * __nullable data, NSURLResponse * __nullable response, NSError * __nullable error) {
                                          if (!error) {
                                              if (data.length > 100)
                                              {
                                                  _cacheblock(data);
                                                  _completeblock([UIImage imageWithData:data]);
                                              }
                                          }
                                          else
                                          {
                                              _failblock(error.localizedDescription);
                                          }
                                      }];
        [task resume];
    }
    else if (dBigImageDownload == _downloadPolicy)
    {

        NSURLSessionDownloadTask *task = [_session downloadTaskWithRequest:_request];
        [task resume];
    }
}

会根据请求的不同,调用不用的NSURLSession两种不同的的工厂方法

- (nullable NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSData * __nullable data, NSURLResponse * __nullable response, NSError * __nullable error))completionHandler;
//无进度条需求的时候,调用此方法

- (nullable NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request;

大图片需要进度条回调的时候调用第二种方法,同时需要完成相应代理方法

- (void)       URLSession:(NSURLSession *)session
             downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
    NSLog(@"didFinishDownloadingToURL, %@", location);
    NSData *finishedData = [NSData dataWithContentsOfURL:location];
    if (finishedData) {
        _cacheblock(finishedData);
        _completeblock([UIImage imageWithData:finishedData]);
    }
}

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
      didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
    {
        if (_progressblock) {
            _progressblock(bytesWritten, totalBytesExpectedToWrite);        
        }
    }

可以从上面的链接中下载工程运行试试,如果有什么不合理的地方欢迎提出!