SDWebImage学习

来源:互联网 发布:淘宝上卖刀具犯法吗 编辑:程序博客网 时间:2024/05/21 15:02

SDWebImage学习

github托管地址:https://github.com/rs/SDWebImage
导入头文件:UIImageView+WebCache.h


使用sd_setImageWithURL:缓存图片:

1 . 方法sd_setImageWithURL:

//简单的加载url中的图片[self.image sd_setImageWithURL:url]

2 . 方法 sd_setImageWithURL: placeholderImage:

//加载完成之前使用图片placeImage作为默认值[self.image1 sd_setImageWithURL:url placeholderImage:placeImage];

3 . 方法sd_setImageWithURL:imagePath2 completed:

//在图片加载完成之后执行block[self.image2 sd_setImageWithURL:imagePath2 completed: ^(UIImage *image, NSError *error,   SDImageCacheType cacheType, NSURL *imageURL) {    NSLog(@"Do something in here...");}];

4 . 方法sd_setImageWithURL:placeholderImage:options:

//options表示缓存方式[self.image sd_setImageWithURL:url              placeholderImage:placeImage                       options:SDWebImageRetryFailed];
options每个选项含义:    //加载失败后重试    SDWebImageRetryFailed = 1 << 0,    //UI交互期间延时下载    SDWebImageLowPriority = 1 << 1,    //只进行内存缓存    SDWebImageCacheMemoryOnly = 1 << 2,    //逐步下载,显示图像也是逐步的    SDWebImageProgressiveDownload = 1 << 3,    //刷新缓存    SDWebImageRefreshCached = 1 << 4,    //后台下载    SDWebImageContinueInBackground = 1 << 5,    //通过设置NSMutableURLRequest.HTTPShouldHandleCookies = YES    //把cookies存储在NSHTTPCookieStore    SDWebImageHandleCookies = 1 << 6,    //允许使用无效的SSL证书    SDWebImageAllowInvalidSSLCertificates = 1 << 7,    //优先下载    SDWebImageHighPriority = 1 << 8,    //延迟显示占位符    SDWebImageDelayPlaceholder = 1 << 9,    //进行任意图形变换    SDWebImageTransformAnimatedImage = 1 << 10,

SDWebImage中我们使用较多的是它提供的UIImageView分类,支持从远程服务器下载并缓存图片。从iOS5.0开始,NSURLCache也可以处理磁盘缓存,那么SDWebImage有什么优势?首先,NSURLCache是缓存原始数据(raw data)到磁盘或内存,因此每次使用的时候需要将原始数据转换成具体的对象,如UIImage等,这会导致额外的数据解析以及内存占用等,而SDWebImage则是缓存UIImage对象在内存,缓存在NSCache中,同时直接保存压缩过的图片到磁盘中;其次,当你第一次在UIImageView中使用image对象的时候,图片的解码是在主线程中运行的!而SDWebImage会强制将解码操作放到子线程中。下图是SDWebImage简单的类图关系:
SDWebImage一些类的调用关系

图片加载调用函数:

- (void)sd_setImageWithURL:(NSURL *)url           placeholderImage:(UIImage *)placeholder                    options:(SDWebImageOptions)options                   progress:(SDWebImageDownloaderProgressBlock)progressBlock                  completed:(SDWebImageCompletionBlock)completedBlock {    //取消正在下载的操作    [self sd_cancelCurrentImageLoad];    //创建对象self和对象url的关联    objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);    /*...*/    if (url) {        //防止循环引用        __weak UIImageView *wself = self;        //由SDWebImageManager负责图片的获取        id <SDWebImageOperation> operation =        [SDWebImageManager.sharedManager downloadImageWithURL:url                                                       options:options                                                      progress:progressBlock                                                     completed:         ^(UIImage *image, NSError *error, SDImageCacheType cacheType,           BOOL finished, NSURL *imageURL) {            /*获取图片到主线层显示*/         }];        [self sd_setImageLoadOperation:operation forKey:@"UIImageViewImageLoad"];    } else {        /*...*/    }}

由以上函数可知图片是从服务端、内存或者硬盘获取是由SDWebImageManager管理的,这个类有几个重要的属性:

//负责管理cache,涉及内存缓存和硬盘保存@property (strong, nonatomic, readonly) SDImageCache *imageCache;//负责从网络下载图片@property (strong, nonatomic, readonly) SDWebImageDownloader *imageDownloader;

manager会根据URL先去imageCache中查找对应的图片,如果没有再使用downloader去下载,并在下载完成缓存图片到imageCache,接着看实现:

- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url       options:(SDWebImageOptions)options      progress:(SDWebImageDownloaderProgressBlock)progressBlock     completed:(SDWebImageCompletionWithFinishedBlock)completedBlock {    /*...*/    //根据URL生成对应的key,没有特殊处理为[url absoluteString];    NSString *key = [self cacheKeyForURL:url];    //去imageCache中寻找图片    operation.cacheOperation =    [self.imageCache queryDiskCacheForKey:key                                     done:^(UIImage *image, SDImageCacheType cacheType) {        /*...*/        //如果图片没有找到,或者采用的SDWebImageRefreshCached选项,则从网络下载        if ((!image || options & SDWebImageRefreshCached) &&            (![self.delegate respondsToSelector:@selector(            imageManager:shouldDownloadImageForURL:)]             || [self.delegate imageManager:self shouldDownloadImageForURL:url])) {            if (image && options & SDWebImageRefreshCached) {                dispatch_main_sync_safe(^{                    //如果图片找到了,但是采用的SDWebImageRefreshCached选项,                    //通知获取到了图片,并再次从网络下载,使NSURLCache重新刷新                    completedBlock(image, nil, cacheType, YES, url);                });            }                /*下载选项设置...*/                //使用imageDownloader开启网络下载                id <SDWebImageOperation> subOperation =                [self.imageDownloader downloadImageWithURL:url                                                   options:downloaderOptions                                                  progress:progressBlock                                                 completed:^(UIImage *downloadedImage,                                                             NSData *data, NSError *error,                                                             BOOL finished) {                            /*...*/                            if (downloadedImage && finished) {                            //下载完成后,先将图片保存到imageCache中,然后主线程返回                                recalculateFromImage:NO imageData:data                                 forKey:key toDisk:cacheOnDisk];                        }                        dispatch_main_sync_safe(^{                            if (!weakOperation.isCancelled) {                                completedBlock(downloadedImage, nil,                                SDImageCacheTypeNone, finished, url);                            }                        });                    }                }            /*...*/            else if (image) {            //在cache中找到图片了,直接返回            dispatch_main_sync_safe(^{                if (!weakOperation.isCancelled) {                    completedBlock(image, nil, cacheType, YES, url);                }            });        /*...*/        }    }];    return operation;}

下面先看downloader从网络下载的过程,下载是放在NSOperationQueue中进行的,默认maxConcurrentOperationCount为6,timeout时间为15s:

- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url          options:(SDWebImageDownloaderOptions)options         progress:(SDWebImageDownloaderProgressBlock)progressBlock        completed:(SDWebImageDownloaderCompletedBlock)completedBlock {    __block SDWebImageDownloaderOperation *operation;    __weak SDWebImageDownloader *wself = self;        /*...*/        //防止NSURLCache和SDImageCache重复缓存        NSMutableURLRequest *request = [[NSMutableURLRequest alloc]                initWithURL:url                 cachePolicy:(options & SDWebImageDownloaderUseNSURLCache ?                             NSURLRequestUseProtocolCachePolicy :                              NSURLRequestReloadIgnoringLocalCacheData)                                         timeoutInterval:timeoutInterval];        request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies);        request.HTTPShouldUsePipelining = YES;        if (wself.headersFilter) {            request.allHTTPHeaderFields =             wself.headersFilter(url, [wself.HTTPHeaders copy]);        }        else {            request.allHTTPHeaderFields = wself.HTTPHeaders;        }        //SDWebImageDownloaderOperation派生自NSOperation,负责图片下载工作        operation = [[wself.operationClass alloc]                     initWithRequest:request                     options:options                     progress:^(NSInteger receivedSize, NSInteger expectedSize) {/*...*/}                     completed:^(UIImage *image, NSData *data,                                  NSError *error, BOOL finished) {/*...*/}                     cancelled:^{/*...*/}];            /*...*/}];    return operation;}

SDWebImageDownloaderOperation派生自NSOperation,通过NSURLConnection进行图片的下载,为了确保能够处理下载的数据,需要在后台运行runloop:

- (void)start {    @synchronized (self) {        /*...*/#if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0        /*开启后台下载*/        if ([self shouldContinueWhenAppEntersBackground]) {            __weak __typeof__ (self) wself = self;            self.backgroundTaskId = [[UIApplication sharedApplication]                                     beginBackgroundTaskWithExpirationHandler:^{                __strong __typeof (wself) sself = wself;                if (sself) {                    [sself cancel];                    [[UIApplication sharedApplication]                     endBackgroundTask:sself.backgroundTaskId];                    sself.backgroundTaskId = UIBackgroundTaskInvalid;                }            }];        }#endif        self.executing = YES;        self.connection = [[NSURLConnection alloc]                           initWithRequest:self.request delegate:self startImmediately:NO];        self.thread = [NSThread currentThread];    }    [self.connection start];    if (self.connection) {        if (self.progressBlock) {            self.progressBlock(0, NSURLResponseUnknownLength);        }        /*广播通知*/        [[NSNotificationCenter defaultCenter]         postNotificationName:SDWebImageDownloadStartNotification object:self];        if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_5_1) {            CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, false);        }        else {            //在默认模式下运行当前runlooprun,直到调用CFRunLoopStop停止运行            CFRunLoopRun();        }        if (!self.isFinished) {            [self.connection cancel];            [self connection:self.connection            didFailWithError:[NSError errorWithDomain:NSURLErrorDomain                                                 code:NSURLErrorTimedOut                                             userInfo:                    @{NSURLErrorFailingURLErrorKey : self.request.URL}]];        }    }    else {        if (self.completedBlock) {            self.completedBlock(nil, nil, [NSError errorWithDomain:NSURLErrorDomain                                                               code:0                                                           userInfo:            @{NSLocalizedDescriptionKey : @"Connection can't be initialized"}], YES);        }    }#if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0    if (self.backgroundTaskId != UIBackgroundTaskInvalid) {        [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskId];        self.backgroundTaskId = UIBackgroundTaskInvalid;    }#endif}

下载过程中,在代理 - (void)connection:(NSURLConnection )connection didReceiveData:(NSData )data中将接收到的数据保存到NSMutableData中,[self.imageData appendData:data],下载完成后在该线程完成图片的解码,并在完成的completionBlock中进行imageCache的缓存:

- (void)connectionDidFinishLoading:(NSURLConnection *)aConnection {    SDWebImageDownloaderCompletedBlock completionBlock = self.completedBlock;    @synchronized(self) {        //停止当前的runLoop        CFRunLoopStop(CFRunLoopGetCurrent());        /*...*/    }    /*...*/    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];            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];}
0 0
原创粉丝点击