iOS开发实战:SDWebImage深度解析

SDWebImage类库简介

SDWebImage 是一个十分经典的iOS获取网络图片的类库,他通过各种缓存的方式把图片保存在内存,磁盘等等的位置,并且在类库内做好的对缓存的管理,使开发者根本无需苦恼网络图片的问题,并且在最近的版本中,也开放了一些让用户可以管理cache的接口,这样使得用户操作起来更加灵活,可以满足自己app的不同需求。

SDWebImage下载图片

SDWebImage 和很多类库一样都是用Operation和队列来控制和管理自己的任务,其中 SDWebImageDownloader就是任务的管理器,他可以通过设置,控制队列的操作数,暂停取消任务,任务的header,认证,一系列的下载可选项( SDWebImageDownloaderOptions ),还可以控制任务的执行任务顺序(first-in-first-out or  last-in-first-out ),并且维护了一个能够保存现有任务的字典,控制不会重复执行相同的任务。但是为了不重复下载但是有多次在不同地方回调,他是这样做的:
任务字典的key是请求的URL,value是一个回调数组,没进来新的任务,就把进度回调和结束回调封装成一个字典,在把这个字典添加到之前的回调数组中,当任务结束的时候,遍历相对应的回调数组,单后清空。

那么他是如何做队列顺序控制的呢?很简单FIFO是队列本身的顺序,而LIFO则是把新的任务使用 addDependency方法变成前一个队列的依赖,一次做到后进先出

这里面有两个点需要说一下:
第一个就是 NSURLRequestCachePolicy网络请求缓存策略,SD使用了 NSURLRequestUseProtocolCachePolicy和 NSURLRequestReloadIgnoringLocalCacheData两种策略可供选择
第二个就是证书认证问题,当你设置了用户名密码的时候,创建 NSURLCredential并且传递到Operation中,进行后面的验证

那么在operation中呢,使用的是NSURLConnection来下载图片,一些东西就写在了他的代理中,下面两个方法就是用来处理缓存和证书的
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge{
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
        [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
    } else {
        if ([challenge previousFailureCount] == 0) {
            if (self.credential) {
                [[challenge sender] useCredential:self.credential forAuthenticationChallenge:challenge];
            } else {
                [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
            }
        } else {
            [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
        }
    }
}

- (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图片管理

这里主要说一下下载图片的流程 

1检查URL,若是url有问题,抛出error

2先判断是否是已经失败的URL请求,若是失败请求并且选项中不包括SDWebImageRetryFailed则抛出Error

3创建operation并且把他加入队列,设置他的cacheOperation

4先检查内存,如果存在缓存,回调,如果没有检查磁盘,如果有记录内存,然后回调,如果没有返回空

5在回调中先检查是否已经被取消,取消的话在队列中删除任务,返回

6若是没有image或者选项中有SDWebImageRefreshCached,再或者代理中的返回是不能下载,去下载图片

7在上面的情况之外,若是存在image,则直接调用最外层回调,返回image,如是没有图片则调用最外层回调,传回空值,这两种情况都会在任务队列中删除任务

8下面回到流程6中说说下载图片,首先根据SDWebImageOptions设置SDWebImageDownloaderOptions,既是设置下载options

9之后开始用imageDownloader下载并且返回下载suboperation,并且设置主operation的取消block,中在队列中删除自身并且取消suboperation

10下面开始下载图片,如是任务取消,不操作,若是失败了,返回失败,并且把url加入到失败列表

11如果下载成功了,首先判断若是设置SDWebImageRefreshCached,并且返回新的image为空,则,不操作

12设下的若是需要转化图片,先利用代理转化图片

13最后保存image到cache

SDWebImage图片缓存

SD自己有两种缓存方式,内存和磁盘两种存储方式,也提供了很多的方法,但是简单来说就是几类的方法,根据key获取image,根据key删除image存储,清除所有缓存,获取文件缓存path等等

内存级别是使用NSCache来存储的
    [self.memCache setObject:image forKey:key cost:image.size.height * image.size.width * image.scale];

磁盘级别的存储是使用NSFileManager处理的
这里面磁盘缓存的文件名是使用keyMD5处理过的字符串
在进行磁盘存储的时候会先判断是否为PNG,来使用不同的获取data的方法

下面是一次使用NSFileManager的方法
NSString *filePath = [self.diskCachePath stringByAppendingPathComponent:fileName];
NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil];
size += [attrs fileSize];  //获取文件大小

NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtPath:self.diskCachePath];
count = [[fileEnumerator allObjects] count];  //获取目录下所有文件

杂七杂八的知识点

1.如何根据一个图片的NSData判断图片的类型呢,我们往往不能单纯的根据文件的后缀就判断文件的类型,而应该根据不同格式的头来判定图片的类型,SD是在NSData+

ImageContentType中对头进行了初步的判断得到相应的MIME,细节可以看这里 http://blog.csdn.net/include1224/article/details/5195470


2.运行时,在SD中使用了一些运行时的方法,比如把某个类的一些数据绑定到运行时中,用的时候在去用某个key取出来使用,大家可以去详细的了解下运行时的东西,非常有用

- (NSMutableDictionary *)imageURLStorage {
    NSMutableDictionary *storage = objc_getAssociatedObject(self, &imageURLStorageKey);
    if (!storage)
    {
        storage = [NSMutableDictionary dictionary];
        objc_setAssociatedObject(self, &imageURLStorageKey, storage, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }

    return storage;
}

SDWebimage 的任务控制也是使用这个做个 每个控件都动态绑定一个字典,根据不同的任务(key)向字典里面存储任务,每次有新任务可以取出operation然后取消。

3针对图片的处理,使用的是ImageI/O框架的内容,通过获取image的信息来显示GIF等,这里看细节   https://developer.apple.com/library/ios/documentation/graphicsimaging/conceptual/ImageIOGuide/ImageIOGuide.pdf

在这里 SD 使用了 CGImageSourceRef 来显示GIF并且 根据图片信息进行方向纠错

4在operation中使用runloop阻塞线程,这是为了线程安全,关于iOS线程的知识参见下面的两个连接

http://www.dreamingwish.com/article/ios-multi-threaded-programming-a-multi-threaded-programming.html

http://www.cocoachina.com/bbs/simple/?t43852.html


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值