• sdwebimage总结


    1. 来自: http://blog.sina.com.cn/s/blog_60342e330101tczw.html

    2. 来自;http://my.oschina.net/cao6793569/blog/497399

    SDWebImage托管在github上。https://github.com/rs/SDWebImage

    这个类库提供一个UIImageView类别以支持加载来自网络的远程图片。具有缓存管理、异步下载、同一个URL下载次数控制和优化等特征。
    使用示范的代码:

    UITableView使用UIImageView+WebCache类(基本应用,UIImageView的一个category)

    前提#import导入UIImageView+WebCache.h文件,然后在tableview的cellForRowAtIndexPath:方法下: 

     - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

         static NSString *MyIdentifier = @"MyIdentifier";

         UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];

        if (cell == nil) {

             cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier] autorelease];

         }

    // Here we use the new provided setImageWithURL: method to load the web image

        [cell.imageView setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"] placeholderImage:[UIImage imageNamed:@"placeholder.png"]];

        cell.textLabel.text = @"My Text";

        return cell;

     }

    基本代码:[imageView setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/image.jpg"]];

    使用SDWebImageManager类:可以进行一些异步加载的工作。

    SDWebImageManager *manager = [SDWebImageManager sharedManager];

    UIImage *cachedImage = [manager imageWithURL:url]; // 将需要缓存的图片加载进来

    if (cachedImage) {

          // 如果Cache命中,则直接利用缓存的图片进行有关操作

          // Use the cached image immediatly

    } else {

          // 如果Cache没有命中,则去下载指定网络位置的图片,并且给出一个委托方法

          // Start an async download

         [manager downloadWithURL:url delegate:self];

    }

    当然你的类要实现SDWebImageManagerDelegate协议,并且要实现协议的webImageManager:didFinishWithImage:方法。

    // 当下载完成后,调用回调方法,使下载的图片显示

    - (void)webImageManager:(SDWebImageManager *)imageManager didFinishWithImage:(UIImage *)image {

        // Do something with the downloaded image

    }

    独立的异步图像下载

    可能会单独用到异步图片下载,则一定要用downloaderWithURL:delegate:来建立一个SDWebImageDownloader实例。

    downloader = [SDWebImageDownloader downloaderWithURL:url delegate:self];

    这样SDWebImageDownloaderDelegate协议的方法imageDownloader:didFinishWithImage:被调用时下载会立即开始并完成。

    独立的异步图像缓存

    SDImageCache类提供一个创建空缓存的实例,并用方法imageForKey:来寻找当前缓存。

    UIImage *myCachedImage = [[SDImageCache sharedImageCache] imageFromKey:myCacheKey];

    存储一个图像到缓存是使用方法storeImage: forKey:

    [[SDImageCache sharedImageCache] storeImage:myImage forKey:myCacheKey];

    默认情况下,图像将被存储在内存缓存和磁盘缓存中。如果仅仅是想内存缓存中,要使用storeImage:forKey:toDisk:方法的第三个参数带一负值

    来替代。

    SDWebImage 支持异步的图片下载+缓存,提供了 UIImageView+WebCacha 的 category,方便使用。纪录一下 SDWebImage 加载图片的流程。

    1. 入口 setImageWithURL:placeholderImage:options: 会先把 placeholderImage 显示,然后 SDWebImageManager 根据 URL 开始处理图片。
    2. 进入 SDWebImageManager-downloadWithURL:delegate:options:userInfo:,交给 SDImageCache 从缓存查找图片是否已经下载 queryDiskCacheForKey:delegate:userInfo:.
    3. 先从内存图片缓存查找是否有图片,如果内存中已经有图片缓存,SDImageCacheDelegate 回调 imageCache:didFindImage:forKey:userInfo: 到 SDWebImageManager。
    4. SDWebImageManagerDelegate 回调 webImageManager:didFinishWithImage: 到 UIImageView+WebCache 等前端展示图片。
    5. 如果内存缓存中没有,生成 NSInvocationOperation 添加到队列开始从硬盘查找图片是否已经缓存。
    6. 根据 URLKey 在硬盘缓存目录下尝试读取图片文件。这一步是在 NSOperation 进行的操作,所以回主线程进行结果回调 notifyDelegate:。
    7. 如果上一操作从硬盘读取到了图片,将图片添加到内存缓存中(如果空闲内存过小,会先清空内存缓存)。SDImageCacheDelegate 回调 imageCache:didFindImage:forKey:userInfo:。进而回调展示图片。
    8. 如果从硬盘缓存目录读取不到图片,说明所有缓存都不存在该图片,需要下载图片,回调 imageCache:didNotFindImageForKey:userInfo:。
    9. 共享或重新生成一个下载器 SDWebImageDownloader 开始下载图片。
    10. 图片下载由 NSURLConnection 来做,实现相关 delegate 来判断图片下载中、下载完成和下载失败。
    11. connection:didReceiveData: 中利用 ImageIO 做了按图片下载进度加载效果。
    12. connectionDidFinishLoading: 数据下载完成后交给 SDWebImageDecoder 做图片解码处理。
    13. 图片解码处理在一个 NSOperationQueue 完成,不会拖慢主线程 UI。如果有需要对下载的图片进行二次处理,最好也在这里完成,效率会好很多。
    14. 在主线程 notifyDelegateOnMainThreadWithInfo: 宣告解码完成,imageDecoder:didFinishDecodingImage:userInfo: 回调给 SDWebImageDownloader。
    15. imageDownloader:didFinishWithImage: 回调给 SDWebImageManager 告知图片下载完成。
    16. 通知所有的 downloadDelegates 下载完成,回调给需要的地方展示图片。
    17. 将图片保存到 SDImageCache 中,内存缓存和硬盘缓存同时保存。写文件到硬盘也在以单独 NSInvocationOperation 完成,避免拖慢主线程。
    18. SDImageCache 在初始化的时候会注册一些消息通知,在内存警告或退到后台的时候清理内存图片缓存,应用结束的时候清理过期图片。
    19. SDWI 也提供了 UIButton+WebCache 和 MKAnnotationView+WebCache,方便使用。
    20. SDWebImagePrefetcher 可以预先下载图片,方便后续使用。

    SDWebImage库的作用:
    通过对UIImageView的类别扩展来实现异步加载替换图片的工作。

    主要用到的对象:
    1、UIImageView (WebCache)类别,入口封装,实现读取图片完成后的回调
    2、SDWebImageManager,对图片进行管理的中转站,记录那些图片正在读取。
    向下层读取Cache(调用SDImageCache),或者向网络读取对象(调用SDWebImageDownloader) 。
    实现SDImageCache和SDWebImageDownloader的回调。
    3、SDImageCache,根据URL的MD5摘要对图片进行存储和读取(实现存在内存中或者存在硬盘上两种实现)
    实现图片和内存清理工作。
    4、SDWebImageDownloader,根据URL向网络读取数据(实现部分读取和全部读取后再通知回调两种方式)

    其他类:
    SDWebImageDecoder,异步对图像进行了一次解压⋯⋯
    目前不明白为什么要做这么道工序。(现在清楚了,功能解释见下文)

    有趣的点:
    1、SDImageCache是怎么做数据管理的?
    SDImageCache分两个部分,一个是内存层面的,一个是硬盘层面的。
    内存层面的相当是个缓存器,以Key-Value的形式存储图片。当内存不够的时候会清除所有缓存图片。
    用搜索文件系统的方式做管理,文件替换方式是以时间为单位,剔除时间大于一周的图片文件。
    当SDWebImageManager向SDImageCache要资源时,先搜索内存层面的数据,如果有直接返回,没有的话去访问磁盘,将图片从磁盘读取出来,然后做Decoder,将图片对象放到内存层面做备份,再返回调用层。

    2、为啥必须做Decoder?
    通过这个博客:http://www.cocoanetics.com/2011/10/avoiding-image-decompression-sickness/
    现在明白了,由于UIImage的imageWithData函数是每次画图的时候才将Data解压成ARGB的图像,
    所以在每次画图的时候,会有一个解压操作,这样效率很低,但是只有瞬时的内存需求。
    为了提高效率通过SDWebImageDecoder将包装在Data下的资源解压,然后画在另外一张图片上,这样这张新图片就不再需要重复解压了。

    这种做法是典型的空间换时间的做法。

    -----------------3 。http://www.myexception.cn/web/1978137.html

    SDWebImage分析--源代码详细分析

    SDWebImage源代码分析

    前言

    关于网上的源代码分析也应该是不少的了,不过对于这个经典的第三方图片下载缓存库的作者还是相当敬佩的。这里还是想就个人理解来分析下,当做笔记加深理解也好。想看大概流程就好的可以看我上一篇博客:传送门:SDWebImage分析–库处理流程分析

    一、UIImage + WebCache 入口:

    我们根据设置Image的时候跳转代码到定义位置其实可以看到几乎所有类型的设置情况都是指向一个函数,只不过是根据你需要的类型作者自行帮你加入了默认项:

    [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 __typeof(self)wself = self;
            id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager downloadImageWithURL:url 
                 options:options 
                progress:progressBlock 
               completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
                if (!wself) return;
                dispatch_main_sync_safe(^{
                    if (!wself) return;
                    if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock)
                    {
                        completedBlock(image, error, cacheType, url);
                        return;
                    }
                    else 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);
                }
            });
        }
    }

    这里代码挺长的。所以我们就按重点来分析。第一句的[self sd_cancelCurrentImageLoad];,我们跳转到该代码来看:

        // Cancel in progress downloader from queue
        NSMutableDictionary *operationDictionary = [self operationDictionary];
        id operations = [operationDictionary objectForKey:key];
        if (operations) {
            if ([operations isKindOfClass:[NSArray class]]) {
                for (id <SDWebImageOperation> operation in operations) {
                    if (operation) {
                        [operation cancel];
                    }
                }
            } else if ([operations conformsToProtocol:@protocol(SDWebImageOperation)]){
                [(id<SDWebImageOperation>) operations cancel];
            }
            [operationDictionary removeObjectForKey:key];
        }

    按照这个代码分析应该是在下载的时候取消当前所有的相同下载队列。但这里我有个问题:如果有某个图片下载还没完成又有同样的下载请求进来就会被取消,这样循环的话这个图片不就没办法被下载。或许分析到最后我们能解决这个疑惑,继续看接下来的代码。 
    第二行: objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

    诶这个函数挺陌生的。我查了一下资料:
    category与associative作为objective-c的扩展机制的两个特性:
    category即类型,可以通过它来扩展方法;associative,可以通过它来扩展属性。
    objc_getAssociatedObject、objc_setAssociatedObject、objc_removeAssociatedObjects都是Obj-c中的外联方法:
    object 参数作为待扩展的对象实例,key作为该对象实例的属性的键,而value就是对象实例的属性的值,policy作为关联的策略。
    plicy对应的枚举策略有:
    enum {
    OBJC_ASSOCIATION_ASSIGN = 0,
    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1,
    OBJC_ASSOCIATION_COPY_NONATOMIC = 3,
    OBJC_ASSOCIATION_RETAIN = 01401,
    OBJC_ASSOCIATION_COPY = 01403
    };
    看了有基础的也就应该大概懂这是什么意思了。这里就拓展下具体就不多说了。
    其实这个用的也算是比较少的东西了。我觉得Category跟Protocol已经可以实现大部分的需求了。
    不过这样看这个也好像挺有用处的。这里就当做顺便增加下知识储备了。
    

    第三句:

        if (!(options & SDWebImageDelayPlaceholder)) {
            dispatch_main_async_safe(^{
                self.image = placeholder;
            });
        }
    这里有必要看一下SDWebImageOptions这个枚举值都是什么东西,我尽可能根据我很普通的英语来粗略翻译下了:
    typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {
        //下载失败则重试,默认是当一个URL下载失败后会被加入黑名单
        SDWebImageRetryFailed = 1 << 0,
        //在用户交互期间延迟下载
        SDWebImageLowPriority = 1 << 1,
        //只允许图片缓存在内存而不允许缓存在磁盘
        SDWebImageCacheMemoryOnly = 1 << 2,
        //采用边下载边显示,默认是下载完成再一次性显示
        SDWebImageProgressiveDownload = 1 << 3,
        //这个太长了,大概意思就是忽略缓存
        SDWebImageRefreshCached = 1 << 4,
        //在iOS4.0+允许应用在后台进行一些操作,这个选项就是允许在后台继续下载
        SDWebImageContinueInBackground = 1 << 5,
        //这个不会翻译。原文:
        //Handles cookies stored in NSHTTPCookieStore by setting
        //NSMutableURLRequest.HTTPShouldHandleCookies = YES;
        SDWebImageHandleCookies = 1 << 6,
        //允许不受信任的SSL证书。实际情况慎用
        SDWebImageAllowInvalidSSLCertificates = 1 << 7,
        //使用高级别的线程权限,默认是等待当前线程完成再进行
        SDWebImageHighPriority = 1 << 8,
        //等待图片下载完成后再显示预加载图片(不明白这个设置有多大用途)
        SDWebImageDelayPlaceholder = 1 << 9,
        SDWebImageTransformAnimatedImage = 1 << 10,
        //下载完成后手动设置图片,默认是下载完成后自动放到ImageView上
        SDWebImageAvoidAutoSetImage = 1 << 11
    };
    

    接下来的代码片:

    id <SDWebImageOperation> operation = 
                [SDWebImageManager.sharedManager downloadImageWithURL:url 
                                                              options:options 
                                                             progress:progressBlock 
                                                            completed:^(UIImage *image, NSError *error, 
                                                            SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) 

    这里便是共享一个SDWebImageManager来进行图片的缓存查找或者下载了。并将这个operation加入到我们在看第一句的函数中那个可变的操作队列字典中。好的,这里就到了最复杂的地方了,让我们跳转到这个manager的函数:

    二、委托到SDWebImageManager进行查找缓存下载图片:
        __block SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new];
        __weak SDWebImageCombinedOperation *weakOperation = operation;
    
        BOOL isFailedUrl = NO;
        @synchronized (self.failedURLs) {
            isFailedUrl = [self.failedURLs containsObject:url];
        }

    先检查下URL有没有在黑名单里面,然后根据URL获得的一个Key去磁盘查找是否存在这个图片

    NSString *key = [self cacheKeyForURL:url];
    
        operation.cacheOperation = 
                    [self.imageCache queryDiskCacheForKey:key 
                                                     done:^(UIImage *image, SDImageCacheType cacheType)

    根据这个函数跳转到其实现的地方:

    三、通过SDWebImageCache查找缓存:
    // 核心代码。
        UIImage *image = [self imageFromMemoryCacheForKey:key];
        if (image) {
            doneBlock(image, SDImageCacheTypeMemory);
            return nil;
        }
        NSOperation *operation = [NSOperation new];
        dispatch_async(self.ioQueue, ^{
            if (operation.isCancelled) {
                return;
            }
    
            @autoreleasepool {
                UIImage *diskImage = [self diskImageForKey:key];
                if (diskImage) {
                    NSUInteger cost = SDCacheCostForImage(diskImage);
                    [self.memCache setObject:diskImage forKey:key cost:cost];
                }
    
                dispatch_async(dispatch_get_main_queue(), ^{
                    doneBlock(diskImage, SDImageCacheTypeDisk);
                });
            }
        });

    可以看到这里是先用传递过来的Key先进行内存缓存查找是否存在这个图片。如果查找成功则返回图片去显示。如果没有,那么这里是新开一个NSOperation的串行队列去磁盘上查找(这里是把io操作用异步线程推到后台,不会干扰到主线程),这里还有个小细节就是作者在这里新建了一个自动释放池,把查找操作放在里面,等查找操作完成后自动销毁产生的额外数据。至于为什么要有这个必要后面再来细致研究下。追踪 [self diskImageForKey:key] 这个函数发现了它的图片存储路径函数为:

    - (NSString *)cachedFileNameForKey:(NSString *)key {
        const char *str = [key UTF8String];
        if (str == NULL) {
            str = "";
        }
        unsigned char r[CC_MD5_DIGEST_LENGTH];
        CC_MD5(str, (CC_LONG)strlen(str), r);
        NSString *filename = [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
                                                        r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10], r[11], r[12], r[13], r[14], r[15]];
    
        return filename;
    }

    有做过安全加密之类算法的一看大概就知道这个是什么了。这就是将文件名进行MD5加密,在信息传输方面也很广泛。对URL进行MD5转换之后就能得到一个唯一的128位长的字符串。对URL进行MD5转换有什么好处呢,可能你觉得每个图片的URL应该也是确定的啊,但每个URL的长度都不一样吧,有的特别长也浪费空间,我想得不是很深,但这样做应该也有便于统一存取的角度吧。然后接下来的工作就是如果从内存照到图片数据则返回,返回的是一个NSData类型,SDImageCache会先对data进行解压处理成相应的图片格式。获得图片的NSData之后这里有一个小片段具体是我还不甚理解的,应该是涉及到图片的解压方面。

            UIImage *image = [UIImage sd_imageWithData:data];
            image = [self scaledImageForKey:key image:image];
            if (self.shouldDecompressImages) {
                image = [UIImage decodedImageWithImage:image];
            }
            return image;

    从磁盘找到图片后则将图片加入到内存缓存之中备用,并通过block返回找到的Image。这里有个函数可以注意下:

     [self.memCache setObject:diskImage forKey:key cost:cost]; 
     这个函数是NSCache中提供的缓存存储函数。有没有感觉很熟悉?
     这让我想到KVO模式中常用到的setValue。
    

    上述查找过程完毕返回结果后,执行以下代码:

    if ((!image || options & SDWebImageRefreshCached) 
        && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] 
        || [self.delegate imageManager:self shouldDownloadImageForURL:url]))

    即是如果上述操作都找不到缓存图片,则通知实现了SDWebImageManagerDelegate 协议的对象根据URL进行图片下载。如果委托没有响应,则进行以下代码到进行图片下载:

    四、新建或共享一个SDWebImageDownloader下载图片:
     id <SDWebImageOperation> subOperation = 
                     [self.imageDownloader downloadImageWithURL:url 
                                                        options:downloaderOptions
                                                       progress:progressBlock 
                                                      completed:^(UIImage *downloadedImage, NSData *data, NSError *error, BOOL finished)

    进入这个函数的实现过程可以看到一个个人觉得挺好的函数,作者把下载过程都放在这个函数的实现回调中:

    - (void)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock 
              andCompletedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock 
                         forURL:(NSURL *)url 
                 createCallback:(SDWebImageNoParamsBlock)createCallback {
        if (url == nil) {
            if (completedBlock != nil) {
                completedBlock(nil, nil, nil, NO);
            }
            return;
        }
    
        dispatch_barrier_sync(self.barrierQueue, ^{
            BOOL first = NO;
            if (!self.URLCallbacks[url]) {
                self.URLCallbacks[url] = [NSMutableArray new];
                first = YES;
            }
    
            // Handle single download of simultaneous download request for the same URL
            NSMutableArray *callbacksForURL = self.URLCallbacks[url];
            NSMutableDictionary *callbacks = [NSMutableDictionary new];
            if (progressBlock) callbacks[kProgressCallbackKey] = [progressBlock copy];
            if (completedBlock) callbacks[kCompletedCallbackKey] = [completedBlock copy];
            [callbacksForURL addObject:callbacks];
            self.URLCallbacks[url] = callbacksForURL;
    
            if (first) {
                createCallback();
            }
        });
    }

    URLCallbacks是一个包含每个图片多组回调信息的字典,key是图片的URL地址,value则是一个数组。由于允许多个图片同时下载,因此可能会有多个线程同时操作URLCallbacks属性。为了保证URLCallbacks操作(添加、删除)的线程安全性,SDWebImageDownloader将这些操作作为一个个任务放到barrierQueue队列中。哦对了,上面提到的NSCache缓存存储步骤因为是线程安全的,所以在存取查找的时候不需要进行额外的维护。

    void dispatch_barrier_async( dispatch_queue_t queue, dispatch_block_t block);
    //这个函数可以设置同步执行的block,在它之后加入队列的block,则等到这个block执行完毕后才开始执行。
    

    而图片的下载根据头文件的定义有两种方式:

    typedef NS_ENUM(NSInteger, SDWebImageDownloaderExecutionOrder) {
        //先进先出,即队列模式(默认属性)
        SDWebImageDownloaderFIFOExecutionOrder,
        //先进后出,即栈模式
        SDWebImageDownloaderLIFOExecutionOrder
    };

    下载部分则是使用NSURLConnetion来进行了。每一个图片的下载都是在一个独立队列中,从而实现并发下载。这一部分就不细说了,有兴趣的可以自己专研下源代码。在github上搜索SDWebImage即可找到原作者的源码。

    SDWebImage之中还编写了清除缓存的方法,如果应用收到内存警告则会自动删除内存缓存的图片(按时间先后顺序)。该库也提供了很多方法供我们去查询与删除图片缓存,其机制也是大同小异,主要实现方法也应该是跟上述讲到的查找缓存的方法类似。具体方法主要有:
    //查找
    - (UIImage *)imageFromMemoryCacheForKey:(NSString *)key;
    - (UIImage *)imageFromDiskCacheForKey:(NSString *)key;
    //移除
    - (void)removeImageForKey:(NSString *)key;
    - (void)removeImageForKey:(NSString *)key withCompletion:(SDWebImageNoParamsBlock)completion;
    - (void)removeImageForKey:(NSString *)key fromDisk:(BOOL)fromDisk;
    - (void)removeImageForKey:(NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(SDWebImageNoParamsBlock)completion;
    //清理磁盘
    - (void)clearDisk;
    - (void)clearDiskOnCompletion:(SDWebImageNoParamsBlock)completion;
    小结

    看到这谨密细致的代码暂时真是只有膜拜的份。

    -----------------4.

    使用SDWebImage加载大量图片后造成内存泄露的解决办法

    http://www.bubuko.com/infodetail-985746.html

    SDWebImage的知名度就不用说了,github上近10k的star,国内外太多的App使用其进行图片加载。

    但是最近在使用过程中发现,在UITableView中不断加载更多的内容,使用SDWebImage会造成内存占用越来越大,导致memory warning最终terminate,稍微找了下问题原因,发现不少开发者都遇到过这个问题,中文的资料没有搜到该问题的解决办法,为了方便国内其他开发者遇到类似问题不浪费时间,这篇blog把解决方法记录如下:

    首先检查了SDWebImage代码中对于memory warning的处理:

    - (void)clearMemory {
        [self.memCache removeAllObjects];
    }

    其中self.memCache是NSCache类型的,可以看到SDWebImage本身对内存警告执行了操作,但是并没有什么X用。

    用Instruments的allocations分析了一下内存使用情况:技术分享

    可以看到内存基本都在decodedImageWithImage:这个方法里被占用了,查看了这个方法的内部实现,感觉还是比较正常的,只好求助google

    最后在github上找到了这条issue:https://github.com/rs/SDWebImage/issues/538

    下面回复很多(看来这个问题很多人都遇到了),甚至SDWebImage的作者本人rs也在下面进行了很多的回复(虽然他的回复并没有什么X用),回复里说到的问题原因和解决办法归纳如下:

    1.rs本人回复的:SDWebImage用到的NSCache会在合适的时候(memory warning)释放内存,很多应用在加载大量图片的时候没有出现这种情况;

    2.这个问题是因为SDWebImage对GIF的支持的代码造成的,去掉相关代码即可;

    3.decodedImageWithImage的实现直接retrun image即可;

    4.https://twitter.com/0xced/status/332252283758845953,这条Twitter也是蛮幽默的,通俗的翻译就是这条Twitter中描述的解决办法拯救了那些因为SDWebImage导致问题的程序员于水火之中。

    挨个试了试上面4中解决方法:

    第一种没什么可说的了,之前分析源码的时候就已经看到,也就是说rs的解决办法没用。

    第二种办法,阅读源码可以发现SDWebImage中对GIF的处理只会针对GIF图片,并不会影响到png或者其他格式的图片,因此这个办法我觉得没用,没有尝试。

    第三种,直接导致内存占用原因由CG raster data变成了ImageIO_PNG_Data,也是没用。

    第四种,最开始我在receive memory warning的时候调用了这段代码

                [[SDImageCache sharedImageCache] setValue:nil forKey:@"memCache"];

    发现依然会出现问题,遂尝试了每次加载更多内容的时候都执行一次,终于内存不再持续增加了,也就是说第四种是最终的解决办法,在后续blog我可能会专门分析一下这个问题出现的原因和解决的原理。

    总结如下:

    在使用SDWebImage加载较多图片造成内存警告时,定期调用

     [[SDImageCache sharedImageCache] setValue:nil forKey:@"memCache"];

    可解决。

    ----------------5.http://blog.sina.com.cn/s/blog_801997310102v08m.html

    SDWebImage 404问题

     
    SDWebImage会把404的图片加入黑名单(也就是当服务器那边返回一个ImageURL时还在加载数据,过了3s后服务器那边把Image。data加载完毕,这不应该这么做,及时加载完也不行了,因为这个url已经在黑名单数据库了),下载的时候选option,  retryFaild ,SDWebImageRetryFailed
    - (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options;

     [self setImageWithURL:[NSURL URLWithString:imaURL] placeholderImage:[UIImageimageNamed:imagePath] options:SDWebImageRetryFailed];
     
    这样设置就解决了!

     ----------------- 6 

    SDWebImage访问HTTPS站点获取图片资源失败解决办法

    分类: IOS

    在使用SDWebImage第三方库是,访问http站点的图片不成问题,但当访问https站点时,会遇到一个提示:

    error:  NSURLConnection/CFURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9813)

    遇到这个提示时,首先说说怎么解决这个问题。

    在SDWebImageDownloaderOperation.m文件中增加以下方法:

    在NSURLConnection 代理方法中处理信任问题:

    - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
    {
        if ([challenge previousFailureCount]== 0) {
            
            //NSURLCredential 这个类是表示身份验证凭据不可变对象。凭证的实际类型声明的类的构造函数来确定。
            NSURLCredential* cre = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
            [challenge.sender useCredential:cre forAuthenticationChallenge:challenge];
        }
        else{
            
        }
    }

    这是NSURLConnection delegate的一个方法。

    HTTPS 超文本传输安全协议(缩写:HTTPS,英语:Hypertext Transfer Protocol Secure)是超文本传输协议SSL/TLS的组合,HTTPS的主要思想是在不安全的网络上创建一安全信道,并可在使用适当的加密包和服务器证书可被验证且可被信任时,对窃听中间人攻击提供合理的保护。HTTPS的信任继承基于预先安装在浏览器中的证书颁发机构(如VeriSign、Microsoft等)(意即“我信任证书颁发机构告诉我应该信任的”)。因此,一个到某网站的HTTPS连接可被信任,如果服务器搭建自己的https 也就是说采用自认证的方式来建立https信道,这样一般在客户端是不被信任的,所以我们一般在浏览器访问一些https站点的时候会有一个提示,问你是否继续。

  • 相关阅读:
    springboot之session、cookie
    Springboot的异步线程池
    spring自带的定时任务功能@EnableScheduling
    SpringBoot+SpringCloud实现登录用户信息在微服务之间的传递
    sss
    sss
    sss
    sss
    sss
    sss
  • 原文地址:https://www.cnblogs.com/lushengcao/p/4894221.html
Copyright © 2020-2023  润新知