美文网首页
SDWebImage前世今生之V1.0版本

SDWebImage前世今生之V1.0版本

作者: _相信自己_ | 来源:发表于2019-03-04 11:40 被阅读0次

    简介

    SDWebImage是一个具有缓存支持的异步图像下载器。为了方便,我们为UIImageView、UIButton、MKAnnotationView等UI元素添加了类别。

    1.0版本2009年12月31日发布

    1.0版本中代码非常简单,没有使用NSURLConnection网络请求,只会从内存当中读取图片以及磁盘读写取文件,以及清除内存与磁盘缓存。
    主要包括下列功能类:
    UIImageView+WebCache 集成了图片下载
    SDWebImageManager 下载管理器
    SDWebImageDownloader 下载器(专门负责下载功能)
    SDImageCache 负责缓存(内存缓存、磁盘缓存)
    SDWebImageManagerDelegate 图片下载完成了
    SDWebImageDownloaderDelegate 图片下载完成了

    2.0版本2010年6月9日发布

    2.0版本类结构上没有发生改变,最大改变增加了NSURLConnection网络请求和下面的一些优化

    1. SDWebImageDownloaderDelegate.h 新增错误回调

    @protocol SDWebImageDownloaderDelegate <NSObject>
    @optional
    
    - (void)imageDownloader:(SDWebImageDownloader *)downloader didFinishWithImage:(UIImage *)image;
    
    //-----------------------2.0版本更新-新增错误回调-----------------------
    - (void)imageDownloader:(SDWebImageDownloader *)downloader didFailWithError:(NSError *)error;
    //---------------------------------end------------------------------
    
    @end
    

    2. SDWebImageManager.m去掉downloadWithURL、cancelForDelegate、didFinishWithImage方法的同步代码块

     - (void)downloadWithURL:(NSURL *)url delegate:(id<SDWebImageManagerDelegate>)delegate{
        if (url == nil || [failedURLs containsObject:url]){
            return;
        }
        SDWebImageDownloader *downloader = [downloaderForURL objectForKey:url];
        if (!downloader){
            downloader = [SDWebImageDownloader downloaderWithURL:url delegate:self];
            [downloaderForURL setObject:downloader forKey:url];
        }
    //-----------------------2.0版本更新-去除@synchronized同步块-----------------------
        [delegates addObject:delegate];
        [downloaders addObject:downloader];
    //-----------------------------------end-----------------------------
    }
    
    - (void)cancelForDelegate:(id<SDWebImageManagerDelegate>)delegate{
    //-----------------------2.0版本更新-去除@synchronized同步块-----------------------
        NSUInteger idx = [delegates indexOfObjectIdenticalTo:delegate];
        if (idx == NSNotFound){
            return;
        }
        SDWebImageDownloader *downloader = [downloaders objectAtIndex:idx];
        [delegates removeObjectAtIndex:idx];
        [downloaders removeObjectAtIndex:idx];
        if (![downloaders containsObject:downloader]){
            [downloader cancel];
            [downloaderForURL removeObjectForKey:downloader.url];
        }
    //-----------------------------------end-----------------------------
    }
    
    - (void)imageDownloader:(SDWebImageDownloader *)downloader didFinishWithImage:(UIImage *)image{
    //-----------------------2.0版本更新-去除@synchronized同步块-----------------------
        for (NSInteger idx = [downloaders count] - 1; idx >= 0; idx--){
            SDWebImageDownloader *aDownloader = [downloaders objectAtIndex:idx];
            if (aDownloader == downloader){
                id<SDWebImageManagerDelegate> delegate = [delegates objectAtIndex:idx];
                if (image && [delegate respondsToSelector:@selector(webImageManager:didFinishWithImage:)]){
                    [delegate performSelector:@selector(webImageManager:didFinishWithImage:) withObject:self withObject:image];
                }
                [downloaders removeObjectAtIndex:idx];
                [delegates removeObjectAtIndex:idx];
            }
        }
    //-----------------------------------end-----------------------------
    
        if (image){
            [[SDImageCache sharedImageCache] storeImage:image forKey:[downloader.url absoluteString]];
        } else {
            [failedURLs addObject:downloader.url];
        }
        [downloaderForURL removeObjectForKey:downloader.url];
    }
    

    3. SDWebImageDownloader.h 的父类由NSOperation更新为NSObject,同时添加了NSURLConnection成员变量。

    //-----------------------2.0版本更新-新增网络请求,继承自NSObject-----------------------
    @interface SDWebImageDownloader : NSObject{
    @private
        NSURL *url;
        NSURLConnection *connection;
        NSMutableData *imageData;
    }
    //-------------------------------end--------------------------------
    
    @property (nonatomic, retain) NSURL *url;
    @property (nonatomic, assign) id<SDWebImageDownloaderDelegate> delegate;
    
    + (id)downloaderWithURL:(NSURL *)url delegate:(id<SDWebImageDownloaderDelegate>)delegate;
    
    //--------------2.0版本更新-新增网络请求,废弃setMaxConcurrentDownloads方法-----------------------
    + (void)setMaxConcurrentDownloads:(NSUInteger)max __attribute__((deprecated));
    - (void)start;
    - (void)cancel;
    //-------------------------------end--------------------------------
    @end
    

    4. SDWebImageDownloader.m增加开始和取消网络请求方法start 、cancel和网络请求回调方法didReceiveData、connectionDidFinishLoading、didFailWithError

    //--------------------------2.0版本更新-网络请求--------------------------
    @interface SDWebImageDownloader ()
    @property (nonatomic, retain) NSURLConnection *connection;
    @property (nonatomic, retain) NSMutableData *imageData;
    @end
    //--------------------------------end---------------------------------
    
    @implementation SDWebImageDownloader
    @synthesize url, delegate, connection, imageData;
    
    //--------------------------2.0版本更新-去除NSOperationQueue--------------------------
    + (id)downloaderWithURL:(NSURL *)url delegate:(id<SDWebImageDownloaderDelegate>)delegate{
        SDWebImageDownloader *downloader = [[SDWebImageDownloader alloc] init];
        downloader.url = url;
        downloader.delegate = delegate;
        [downloader start];
        return downloader;
    }
    //--------------------------------end---------------------------------
    
    + (void)setMaxConcurrentDownloads:(NSUInteger)max{
        // NOOP
    }
    
    //--------------------------2.0版本更新-网络请求--------------------------
    - (void)start{
        NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:15];
        self.connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
        [connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
        [connection start];
    
        if (connection){
            self.imageData = [NSMutableData data];
        } else {
            if ([delegate respondsToSelector:@selector(imageDownloader:didFailWithError:)]){
                [delegate performSelector:@selector(imageDownloader:didFailWithError:) withObject:self withObject:nil];
            }
        }
    }
    - (void)cancel{
        if (connection){
            [connection cancel];
            self.connection = nil;
        }
    }
    //--------------------------end--------------------------
    
    //--------------------------2.0版本更新-请求回调--------------------------
    - (void)connection:(NSURLConnection *)aConnection didReceiveData:(NSData *)data{
        [imageData appendData:data];
    }
    
    - (void)connectionDidFinishLoading:(NSURLConnection *)aConnection{
        UIImage *image = [[UIImage alloc] initWithData:imageData];
        self.imageData = nil;
        self.connection = nil;
        if ([delegate respondsToSelector:@selector(imageDownloader:didFinishWithImage:)]){
            [delegate performSelector:@selector(imageDownloader:didFinishWithImage:) withObject:self withObject:image];
        }
    }
    
    - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
        if ([delegate respondsToSelector:@selector(imageDownloader:didFailWithError:)]){
            [delegate performSelector:@selector(imageDownloader:didFailWithError:) withObject:self withObject:error];
        }
        self.connection = nil;
        self.imageData = nil;
    }
    //--------------------------end--------------------------
    
    @end
    

    5. SDImageCache.m新增后台通知UIApplicationDidEnterBackgroundNotification,当进入后台模式清空内存缓存,优化内存存储。

    - (instancetype)init{
        self = [super init];
        if (self) {
            //内存缓存
            memCache = [[NSMutableDictionary alloc] init];
            //磁盘缓存
            NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
            //磁盘目录
            diskCachePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"ImageCache"];
            //创建目录
            if (![[NSFileManager defaultManager] fileExistsAtPath:diskCachePath]){
                [[NSFileManager defaultManager] createDirectoryAtPath:diskCachePath withIntermediateDirectories:YES attributes:nil error:NULL];
            }
            
            //初始化缓存队列
            cacheInQueue = [[NSOperationQueue alloc] init];
            cacheInQueue.maxConcurrentOperationCount = 2;
            //订阅应用程序事件
            //应用程序终止,里面回调didReceiveMemoryWarning方法,清空内存
            [[NSNotificationCenter defaultCenter] addObserver:self
                                                     selector:@selector(didReceiveMemoryWarning:)
                                                         name:UIApplicationDidReceiveMemoryWarningNotification
                                                       object:nil];
            //应用程序终止,里面回调willTerminate方法,清空磁盘
            [[NSNotificationCenter defaultCenter] addObserver:self
                                                     selector:@selector(willTerminate)
                                                         name:UIApplicationWillTerminateNotification
                                                       object:nil];
    
            //-----------------------2.0版本更新-新增后台通知-----------------------
            UIDevice *device = [UIDevice currentDevice];
            if ([device respondsToSelector:@selector(isMultitaskingSupported)] && device.multitaskingSupported){
                [[NSNotificationCenter defaultCenter] addObserver:self
                                                         selector:@selector(clearMemory)
                                                             name:UIApplicationDidEnterBackgroundNotification
                                                           object:nil];
            }
            //--------------------------------end--------------------------------
        }
        return self;
    }
    

    2.1版本2010年6月12日发布

    1. UIImageView+WebCache 新增取消图片加载

    @interface UIImageView (WebCache) <SDWebImageManagerDelegate>
    
    - (void)setImageWithURL:(NSURL *)url;
    - (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder;
    
    //----------------------2.1版本更新-新增-取消图片加载--------------------
    - (void)cancelCurrentImageLoad;
    //------------------------------end----------------------------------
    
    @end
    
    //-----------------------2.1版本更新-取消图片加载-----------------------
    - (void)cancelCurrentImageLoad{
        [[SDWebImageManager sharedManager] cancelForDelegate:self];
    }
    //------------------------------end----------------------------------
    
    

    2. SDImageCache 缓存器做了优化,兼容iPhone4增加了宏定义

            //-----------------------2.1版本更新-系统版本兼容宏定义-----------------------
            #ifdef __IPHONE_4_0
            UIDevice *device = [UIDevice currentDevice];
            if ([device respondsToSelector:@selector(isMultitaskingSupported)] && device.multitaskingSupported){
                [[NSNotificationCenter defaultCenter] addObserver:self
                                                         selector:@selector(clearMemory)
                                                             name:UIApplicationDidEnterBackgroundNotification
                                                           object:nil];
            }
            #endif
            //--------------------------------end--------------------------------
        }
    

    2.2版本2010年8月29日发布

    更新说明:如果图片格式是PNG或GIF,当存储到磁盘进行缓存时候,不要将图像转换为JPEG,因为他能够节省CPU和内存以及alpha通道/图像的清晰度。

    1. SDWebImageDownloader 新增加了属性,在主线程下载和优化了回调处理

    @property (nonatomic, retain) NSURL *url;
    @property (nonatomic, assign) id<SDWebImageDownloaderDelegate> delegate;
    //-------------2.2版本更新-新增-新增属性-------------------
    @property (nonatomic, retain) NSMutableData *imageData;
    //--------------------------end-------------------------
    
    + (id)downloaderWithURL:(NSURL *)url delegate:(id<SDWebImageDownloaderDelegate>)delegate{
        SDWebImageDownloader *downloader = [[SDWebImageDownloader alloc] init];
        downloader.url = url;
        downloader.delegate = delegate;
    //-------------2.2版本更新-在主线程下载-------------------
        [downloader performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:YES];
    //--------------------------end-------------------------
        return downloader;
    }
    
    - (void)connectionDidFinishLoading:(NSURLConnection *)aConnection{
        //------------------2.2版本更新-优化----------------------
        self.connection = nil;
        //下载器下载完成
        if ([delegate respondsToSelector:@selector(imageDownloaderDidFinish:)]){
            [delegate performSelector:@selector(imageDownloaderDidFinish:) withObject:self];
        }
        if ([delegate respondsToSelector:@selector(imageDownloader:didFinishWithImage:)]){
            UIImage *image = [[UIImage alloc] initWithData:imageData];
            [delegate performSelector:@selector(imageDownloader:didFinishWithImage:) withObject:self withObject:image];
        }
        //--------------------------end--------------------------
    }
    
    
    

    2. 缓存器->SDImageCache 新增了属性和方法重载,增加了二进制NSData数据存储到磁盘

    @interface SDImageCache : NSObject{
        NSMutableDictionary *memCache;
        NSString *diskCachePath;
        NSOperationQueue *cacheInQueue;
        //-------------2.2版本更新-新增-方法重载-功能抽象-------------------
        NSMutableDictionary *storeDataQueue;
        //--------------------------------end------------------------------
    }
    
    //-------------2.2版本更新-新增-方法重载-功能抽象-------------------
    - (void)storeImage:(UIImage *)image imageData:(NSData *)data forKey:(NSString *)key toDisk:(BOOL)toDisk;
    //-------------------------------end---------------------------
    
    //-------------2.2版本更新-新增-方法重载-功能抽象-------------------
    - (void)storeImage:(UIImage *)image forKey:(NSString *)key{
        [self storeImage:image imageData:nil forKey:key toDisk:YES];
    }
    - (void)storeImage:(UIImage *)image forKey:(NSString *)key toDisk:(BOOL)toDisk{
        [self storeImage:image imageData:nil forKey:key toDisk:toDisk];
    }
    - (void)storeImage:(UIImage *)image imageData:(NSData *)data forKey:(NSString *)key toDisk:(BOOL)toDisk{
        if (!image || !key){
            return;
        }
        if (toDisk && !data){
            return;
        }
        [memCache setObject:image forKey:key];
        if (toDisk){
            [storeDataQueue setObject:data forKey:key];
            [cacheInQueue addOperation:[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(storeKeyToDisk:) object:key]];
        }
    }
    //-------------------------------end---------------------------
    
    - (void)storeKeyToDisk:(NSString *)key{
        //-------------2.2版本更新-磁盘缓存-------------------
        NSFileManager *fileManager = [[NSFileManager alloc] init];
        NSData *data = [storeDataQueue objectForKey:key];
        if (data){
            //存储二进制->NSData
            [fileManager createFileAtPath:[self cachePathForKey:key] contents:data attributes:nil];
            @synchronized(storeDataQueue){
                [storeDataQueue removeObjectForKey:key];
            }
            //----------------------end------------------------
        } else {
            //存储图片->UIImage
            UIImage *image = [self imageFromKey:key fromDisk:YES];
            if (image){
                [fileManager createFileAtPath:[self cachePathForKey:key] contents:UIImageJPEGRepresentation(image, (CGFloat)1.0) attributes:nil];
            }
        }
    }
    

    3. SDWebImageDownloaderDelegate 新增了回调imageDownloaderDidFinish针对于imageData它而设计,后面版本迭代会更新具体实现暂时没有。

    //-----------------------2.2版本更新-下载完成回调-----------------------
    - (void)imageDownloaderDidFinish:(SDWebImageDownloader *)downloader;
    //---------------------------------end------------------------------
    

    2.3版本2010年9月17日发布

    1. SDImageCacheDelegate 新增该代理类

    //-------------------2.3版本更新-缓存回调-------------------
    @class SDImageCache;
    @protocol SDImageCacheDelegate <NSObject>
    @optional
    //缓存过程中有图片,缓存成功
    - (void)imageCache:(SDImageCache *)imageCache didFindImage:(UIImage *)image forKey:(NSString *)key userInfo:(NSDictionary *)info;
    //缓存过程中没有图片,缓存失败了
    - (void)imageCache:(SDImageCache *)imageCache didNotFindImageForKey:(NSString *)key userInfo:(NSDictionary *)info;
    @end
    //---------------------------end-------------------------
    

    2. UIImageView + WebCache 抽象缓存逻辑到管理器中

    //-------------------2.3版本更新-优化处理-------------------
    - (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder{
        SDWebImageManager *manager = [SDWebImageManager sharedManager];
        [manager cancelForDelegate:self];
        self.image = placeholder;
        if (url){
            [manager downloadWithURL:url delegate:self];
        }
    }
    //---------------------------end-------------------------
    

    3. SDWebImageManager 实现SDImageCacheDelegate协议

            缓存代理,异步检查磁盘上的缓存,这样我们就不会阻塞主线程缓存代理回调实现。
    
    - (void)downloadWithURL:(NSURL *)url delegate:(id<SDWebImageManagerDelegate>)delegate{
        if (!url || !delegate || [failedURLs containsObject:url]){
            return;
        }
        //-------------------2.3版本更新-避免阻塞主线程-------------------
        //检查磁盘上的缓存异步,这样我们就不会阻塞主线程
        NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:delegate, @"delegate", url, @"url", nil];
        [[SDImageCache sharedImageCache] queryDiskCacheForKey:[url absoluteString] delegate:self userInfo:info];
        //---------------------------end-------------------------
    }
    
    //--------------------2.3版本更新--实现协议SDImageCacheDelegate功能----------
    - (void)imageCache:(SDImageCache *)imageCache didFindImage:(UIImage *)image forKey:(NSString *)key userInfo:(NSDictionary *)info{
        id<SDWebImageManagerDelegate> delegate = [info objectForKey:@"delegate"];
        if ([delegate respondsToSelector:@selector(webImageManager:didFinishWithImage:)]){
            [delegate performSelector:@selector(webImageManager:didFinishWithImage:) withObject:self withObject:image];
        }
    }
    
    - (void)imageCache:(SDImageCache *)imageCache didNotFindImageForKey:(NSString *)key userInfo:(NSDictionary *)info{
        NSURL *url = [info objectForKey:@"url"];
        id<SDWebImageManagerDelegate> delegate = [info objectForKey:@"delegate"];
        //为相同的URL共享相同的下载加载程序,所以我们不会多次下载相同的URL
        SDWebImageDownloader *downloader = [downloaderForURL objectForKey:url];
        if (!downloader){
            downloader = [SDWebImageDownloader downloaderWithURL:url delegate:self];
            [downloaderForURL setObject:downloader forKey:url];
        }
        [delegates addObject:delegate];
        [downloaders addObject:downloader];
    }
    //------------------------------------end---------------------------------
    

    3. SDImageCache

    • 新增了属性cacheOutQueue避免阻塞主线程,
    • 新增异步读取缓存方法queryDiskCacheForKey两个缓存:内存缓存、磁盘缓存读取是耗时操作
    • 新增了notifyDelegate方法通知所有的缓存代理
    • 新增了queryDiskCacheOperation方法2.3版本网络请求还没有完善
    @interface SDImageCache : NSObject{
        NSMutableDictionary *memCache;
        NSString *diskCachePath;
        NSOperationQueue *cacheInQueue;
        
        //-------------------2.3版本更新-避免阻塞主线程--------------------
        NSOperationQueue *cacheOutQueue;
        //--------------------------------end--------------------------
    }
    
    - (UIImage *)imageFromKey:(NSString *)key;
    - (UIImage *)imageFromKey:(NSString *)key fromDisk:(BOOL)fromDisk;
    
    //---------------2.3版本更新-新增功能->异步方法--------------------
    - (void)queryDiskCacheForKey:(NSString *)key delegate:(id<SDImageCacheDelegate>)delegate userInfo:(NSDictionary *)info;
    //-------------------------------end---------------------------
    @end
    
    //---------------2.3版本更新-新增功能->异步方法--------------------
    - (void)notifyDelegate:(NSDictionary *)arguments{
        NSString *key = [arguments objectForKey:@"key"];
        id <SDImageCacheDelegate> delegate = [arguments objectForKey:@"delegate"];
        NSDictionary *info = [arguments objectForKey:@"userInfo"];
        UIImage *image = [arguments objectForKey:@"image"];
        if (image){
            [memCache setObject:image forKey:key];
            if ([delegate respondsToSelector:@selector(imageCache:didFindImage:forKey:userInfo:)]){
                [delegate imageCache:self didFindImage:image forKey:key userInfo:info];
            }
        } else {
            if ([delegate respondsToSelector:@selector(imageCache:didNotFindImageForKey:userInfo:)]){
                [delegate imageCache:self didNotFindImageForKey:key userInfo:info];
            }
        }
    }
    - (void)queryDiskCacheOperation:(NSDictionary *)arguments{
        NSString *key = [arguments objectForKey:@"key"];
        NSMutableDictionary *mutableArguments = [arguments mutableCopy];
        UIImage *image = [[UIImage alloc] initWithContentsOfFile:[self cachePathForKey:key]];
        if (image){
            [mutableArguments setObject:image forKey:@"image"];
        }
        [self performSelectorOnMainThread:@selector(notifyDelegate:) withObject:mutableArguments waitUntilDone:NO];
    }
    - (void)queryDiskCacheForKey:(NSString *)key delegate:(id <SDImageCacheDelegate>)delegate userInfo:(NSDictionary *)info{
        if (!delegate){
            return;
        }
        if (!key){
            if ([delegate respondsToSelector:@selector(imageCache:didNotFindImageForKey:userInfo:)]){
                [delegate imageCache:self didNotFindImageForKey:key userInfo:info];
            }
            return;
        }
        //首先检查内存缓存…
        UIImage *image = [memCache objectForKey:key];
        if (image){
            //从缓存中读取到了图片
            //立即通知delegate,不需要去异步
            if ([delegate respondsToSelector:@selector(imageCache:didFindImage:forKey:userInfo:)]){
                [delegate imageCache:self didFindImage:image forKey:key userInfo:info];
            }
            return;
        }
        //缓存中没有
        NSMutableDictionary *arguments = [NSMutableDictionary dictionaryWithCapacity:3];
        [arguments setObject:key forKey:@"key"];
        [arguments setObject:delegate forKey:@"delegate"];
        if (info){
            [arguments setObject:info forKey:@"userInfo"];
        }
        [cacheOutQueue addOperation:[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(queryDiskCacheOperation:) object:arguments]];
    }
    //------------------------end------------------------
    

    相关文章

      网友评论

          本文标题:SDWebImage前世今生之V1.0版本

          本文链接:https://www.haomeiwen.com/subject/lmtruqtx.html