zoukankan      html  css  js  c++  java
  • SDWebImage源代码解析(二)

    上一篇:SDWebImage源代码解析(一)


    2、缓存

    为了降低网络流量的消耗。我们都希望下载下来的图片缓存到本地。下次再去获取同一张图片时。能够直接从本地获取,而不再从远程server获取。这样做的还有一个优点是提升了用户体验,用户第二次查看同一幅图片时,能高速从本地获取图片直接呈现给用户。

    SDWebImage提供了对图片缓存的支持,而该功能是由SDImageCache类来完毕的。该类负责处理内存缓存及一个可选的磁盘缓存。当中磁盘缓存的写操作是异步的,这样就不会对UI操作造成影响。


    2.1 内存缓存及磁盘缓存

    内存缓存的处理是使用NSCache对象来实现的。NSCache是一个类似于集合的容器。它存储key-value对,这一点类似于NSDictionary类。

    我们通常使用缓存来暂时存储短时间使用但创建昂贵的对象。

    重用这些对象能够优化性能,由于它们的值不须要又一次计算。另外一方面,这些对象对于程序来说不是紧要的,在内存紧张时会被丢弃。

    磁盘缓存的处理则是使用NSFileManager对象来实现的。

    图片存储的位置是位于Cache目录。

    另外。SDImageCache还定义了一个串行队列,来异步存储图片。

    内存缓存与磁盘缓存相关变量的声明及定义例如以下:

    @interface SDImageCache ()
    @property (strong, nonatomic) NSCache *memCache;
    @property (strong, nonatomic) NSString *diskCachePath;
    @property (strong, nonatomic) NSMutableArray *customPaths;
    @property (SDDispatchQueueSetterSementics, nonatomic) dispatch_queue_t ioQueue;
    @end
    - (id)initWithNamespace:(NSString *)ns {
        if ((self = [super init])) {
      NSString *fullNamespace = [@"com.hackemist.SDWebImageCache." stringByAppendingString:ns];
      ...
      _ioQueue = dispatch_queue_create("com.hackemist.SDWebImageCache", DISPATCH_QUEUE_SERIAL);
      ...
      // 初始化内存缓存
      _memCache = [[NSCache alloc] init];
      _memCache.name = fullNamespace;
      // 初始化磁盘缓存
      NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
      _diskCachePath = [paths[0] stringByAppendingPathComponent:fullNamespace];
      dispatch_sync(_ioQueue, ^{
          _fileManager = [NSFileManager new];
      });
      ...
        }
        return self;
    }

    SDImageCache提供了大量方法来缓存、获取、移除及清空图片。而对于每一个图片。为了方便地在内存或磁盘中对它进行这些操作,我们须要一个key值来索引它。

    在内存中。我们将其作为NSCache的key值。而在磁盘中。我们用这个key作为图片的文件名称。

    对于一个远程server下载的图片,其url是作为这个key的最佳选择了。

    我们在后面会看到这个key值的重要性。


    2.2 存储图片

    我们先来看看图片的缓存操作,当下载完图片后。会先将图片保存到NSCache中,并把图片像素大小作为该对象的cost值,同一时候假设须要保存到硬盘。会先推断图片的格式,PNG或者JPEG。并保存相应的NSData到缓存路径中,文件名称为URL的MD5值:

    - (NSString *)cachedFileNameForKey:(NSString *)key {
        //依据key生成相应的MD5值作为文件名称
        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;
    }

    缓存操作的基础方法是 -storeImage:recalculateFromImage:imageData:forKey:toDisk,它的详细实现例如以下:

    - (void)storeImage:(UIImage *)image recalculateFromImage:(BOOL)recalculate imageData:(NSData *)imageData forKey:(NSString *)key toDisk:(BOOL)toDisk {
      ...
      // 1. 内存缓存,将其存入NSCache中,同一时候传入图片的消耗值。cost为像素值
      [self.memCache setObject:image forKey:key cost:image.size.height * image.size.width * image.scale * image.scale];
      if (toDisk) {
        // 2. 假设确定须要磁盘缓存。则将缓存操作作为一个任务放入ioQueue中
        dispatch_async(self.ioQueue, ^{
          NSData *data = imageData;
          if (image && (recalculate || !data)) {
    #if TARGET_OS_IPHONE
            // 3. 须要确定图片是PNG还是JPEG。

    PNG图片easy检測,由于有一个唯一签名。PNG图像的前8个字节总是包括下面值:137 80 78 71 13 10 26 10 // 在imageData为nil的情况下假定图像为PNG。我们将其当作PNG以避免丢失透明度。而当有图片数据时。我们检測其前缀,确定图片的类型 BOOL imageIsPng = YES; if ([imageData length] >= [kPNGSignatureData length]) { imageIsPng = ImageDataHasPNGPreffix(imageData); } if (imageIsPng) { data = UIImagePNGRepresentation(image); } else { data = UIImageJPEGRepresentation(image, (CGFloat)1.0); } #else data = [NSBitmapImageRep representationOfImageRepsInArray:image.representations usingType: NSJPEGFileType properties:nil]; #endif } // 4. 创建缓存文件并存储图片 if (data) { if (![_fileManager fileExistsAtPath:_diskCachePath]) { [_fileManager createDirectoryAtPath:_diskCachePath withIntermediateDirectories:YES attributes:nil error:NULL]; } //保存data到指定的路径中 [_fileManager createFileAtPath:[self defaultCachePathForKey:key] contents:data attributes:nil]; } }); } }


    2.3 查询图片

    假设我们想在内存磁盘中查询是否有key指定的图片。则能够分别使用下面方法:
    - (UIImage *)imageFromMemoryCacheForKey:(NSString *)key;
    - (UIImage *)imageFromDiskCacheForKey:(NSString *)key;
    而假设仅仅是想查看本地是否在key指定的图片,则无论是在内存还是在磁盘上,则能够使用 -queryDiskCacheForKey: done: 方法:
    每次向SDWebImageCache索取图片的时候,会先依据图片URL相应的key值先检查内存中是否有相应的图片,假设有则直接返回;假设没有则在ioQueue中去硬盘中查找,当中文件名称是是依据URL生成的MD5值。找到之后先将图片缓存在内存中。然后再把图片返回。


    - (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock {
      ...
      // 1. 首先查看内存缓存,假设查找到。则直接回调doneBlock并返回
      UIImage *image = [self imageFromDiskCacheForKey:key];
      if (image) {
        doneBlock(image, SDImageCacheTypeMemory);
        return nil;
      }
      // 2. 假设内存中没有。则在磁盘中查找。假设找到,则将其放到内存缓存,并调用doneBlock回调
      NSOperation *operation = [NSOperation new];
      dispatch_async(self.ioQueue, ^{
        if (operation.isCancelled) {
          return;
        }
        //创建自己主动释放池。内存及时释放
        @autoreleasepool {
          UIImage *diskImage = [self diskImageForKey:key];
          if (diskImage) {
            CGFloat cost = diskImage.size.height * diskImage.size.width * diskImage.scale * diskImage.scale;
            //缓存到NSCache中
            [self.memCache setObject:diskImage forKey:key cost:cost];
          }
          dispatch_async(dispatch_get_main_queue(), ^{
            doneBlock(diskImage, SDImageCacheTypeDisk);
          });
        }
      });
      return operation;
    }

    在硬盘查询的时候,会在后台将NSData转成UIImage,并完毕相关的解码工作:
    - (UIImage *)diskImageForKey:(NSString *)key {
        NSData *data = [self diskImageDataBySearchingAllPathsForKey:key];
        if (data) {
            UIImage *image = [UIImage sd_imageWithData:data];
            image = [self scaledImageForKey:key image:image];
            if (self.shouldDecompressImages) {
                image = [UIImage decodedImageWithImage:image];
            }
            return image;
        }
        else {
            return nil;
        }
    }


    2.4 移除图片

      图片的移除操作则能够使用下面方法:
    - (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;

    我们能够选择同一时候移除内存及磁盘上的图片。


    2.5 清理图片

    磁盘缓存图片的清理操作能够分为全然清空和部分清理。

    全然清空操作是直接把缓存的目录移除。清空操作有下面两个方法:

    - (void)clearDisk;
    - (void)clearDiskOnCompletion:(SDWebImageNoParamsBlock)completion;
    而部分清理则是依据我们设定的一些參数值来移除一些文件,这里主要有两个指标:文件的缓存有效期最大缓存空间大小
    文件的缓存有效期能够通过maxCacheAge属性来设置,默认是1周的时间。假设文件的缓存时间超过这个时间值,则将其移除。

    而最大缓存空间大小是通过maxCacheSize属性来设置的,假设全部缓存文件的总大小超过这一大小,则会依照文件最后改动时间的逆序,以每次一半的递归来移除那些过早的文件,直到缓存的实际大小小于我们设置的最大使用空间。
    清理的操作在-cleanDiskWithCompletionBlock:方法中,事实上现例如以下:
    - (void)cleanDiskWithCompletionBlock:(SDWebImageNoParamsBlock)completionBlock {
      dispatch_async(self.ioQueue, ^{
        NSURL *diskCacheURL = [NSURL fileURLWithPath:self.diskCachePath isDirectory:YES];
        NSArray *resourceKeys = @[NSURLIsDirectoryKey, NSURLContentModificationDateKey, NSURLTotalFileAllocatedSizeKey];
        // 1. 该枚举器预先获取缓存文件的实用的属性
        NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtURL:diskCacheURL
                               includingPropertiesForKeys:resourceKeys
                                        options:NSDirectoryEnumerationSkipsHiddenFiles
                                     errorHandler:NULL];
        NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:-self.maxCacheAge];
        NSMutableDictionary *cacheFiles = [NSMutableDictionary dictionary];
        NSUInteger currentCacheSize = 0;
        // 2. 枚举缓存目录中全部文件,该迭代有两个目的:移除比过期日期更老的文件;存储文件属性以备后面运行基于缓存大小的清理操作
        NSMutableArray *urlsToDelete = [[NSMutableArray alloc] init];
        for (NSURL *fileURL in fileEnumerator) {
          NSDictionary *resourceValues = [fileURL resourceValuesForKeys:resourceKeys error:NULL];
          // 3. 跳过目录
          if ([resourceValues[NSURLIsDirectoryKey] boolValue]) {
            continue;
          }
          // 4. 移除早于有效期的老文件
          NSDate *modificationDate = resourceValues[NSURLContentModificationDateKey];
          if ([[modificationDate laterDate:expirationDate] isEqualToDate:expirationDate]) {
            [urlsToDelete addObject:fileURL];
            continue;
          }
          // 5. 存储文件的引用并计算全部文件的总大小。以备后用
          NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
          currentCacheSize += [totalAllocatedSize unsignedIntegerValue];
          [cacheFiles setObject:resourceValues forKey:fileURL];
        }
        for (NSURL *fileURL in urlsToDelete) {
          [_fileManager removeItemAtURL:fileURL error:nil];
        }
        // 6.假设磁盘缓存的大小大于我们配置的最大大小,则运行基于文件大小的清理。我们首先删除最老的文件
        if (self.maxCacheSize > 0 && currentCacheSize > self.maxCacheSize) {
          // 7. 以设置的最大缓存大小的一半作为清理目标
          const NSUInteger desiredCacheSize = self.maxCacheSize / 2;
          // 8. 依照最后改动时间来排序剩下的缓存文件
          NSArray *sortedFiles = [cacheFiles keysSortedByValueWithOptions:NSSortConcurrent
                                  usingComparator:^NSComparisonResult(id obj1, id obj2) {
                                    return [obj1[NSURLContentModificationDateKey] compare:obj2[NSURLContentModificationDateKey]];
                                  }];
          // 9. 删除文件,直到缓存总大小降到我们期望的大小
          for (NSURL *fileURL in sortedFiles) {
            if ([_fileManager removeItemAtURL:fileURL error:nil]) {
              NSDictionary *resourceValues = cacheFiles[fileURL];
              NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
              currentCacheSize -= [totalAllocatedSize unsignedIntegerValue];
              if (currentCacheSize < desiredCacheSize) {
                break;
              }
            }
          }
        }
                    if (completionBlock) {
          dispatch_async(dispatch_get_main_queue(), ^{
            completionBlock();
          });
        }
      });
    }


    2.6 小结

    以上分析了图片缓存操作,当然。除了上面讲的几个操作。SDImageCache类还提供了一些辅助方法。如获取缓存大小、缓存中图片的数量、推断缓存中是否存在某个key指定的图片。

    另外,SDImageCache类提供了一个单例方法的实现。所以我们能够将其当作单例对象来处理。



    SDWebImageManager

    在实际的运用中,我们并不直接使用SDWebImageDownloader类及SDImageCache类来运行图片的下载及缓存。

    为了方便用户的使用,SDWebImage提供了SDWebImageManager对象来管理图片的下载与缓存。

    并且我们经经常使用到的诸如UIImageView+WebCache等控件的分类都是基于SDWebImageManager对象的。

    该对象将一个下载器和一个图片缓存绑定在一起,并对外提供两个仅仅读属性来获取它们,例如以下代码所看到的:

    @interface SDWebImageManager : NSObject
    
    @property (weak, nonatomic) id <SDWebImageManagerDelegate> delegate;
    
    @property (strong, nonatomic, readonly) SDImageCache *imageCache;
    @property (strong, nonatomic, readonly) SDWebImageDownloader *imageDownloader;
    
    ...
    
    @end
    从上面的代码中我们还能够看到有一个delegate属性,其是一个id<SDWebImageManagerDelegate>对象。SDWebImageManagerDelegate声明了两个可选实现的方法,例如以下所看到的:

    // 控制当图片在缓存中没有找到时。应该下载哪个图片
    - (BOOL)imageManager:(SDWebImageManager *)imageManager shouldDownloadImageForURL:(NSURL *)imageURL;
    
    // 同意在图片已经被下载完毕且被缓存到磁盘或内存前马上转换
    - (UIImage *)imageManager:(SDWebImageManager *)imageManager transformDownloadedImage:(UIImage *)image withURL:(NSURL *)imageURL;

    这两个代理方法会在SDWebImageManager的-downloadImageWithURL:options:progress:completed:方法中调用,而这种方法是SDWebImageManager类的核心所在。

    我们来看看它的详细实现:

    - (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url
                         options:(SDWebImageOptions)options
                        progress:(SDWebImageDownloaderProgressBlock)progressBlock
                         completed:(SDWebImageCompletionWithFinishedBlock)completedBlock {
      ...
      // 前面省略n行。主要作了例如以下处理:
      // 1. 推断url的合法性
      // 2. 创建SDWebImageCombinedOperation对象
      // 3. 查看url是否是之前下载失败过的
      // 4. 假设url为nil。或者在不可重试的情况下是一个下载失败过的url,则直接返回操作对象并调用完毕回调
      operation.cacheOperation = [self.imageCache queryDiskCacheForKey:key done:^(UIImage *image, SDImageCacheType cacheType) {
        ...
        if ((!image || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url])) {
          // 下载
          id <SDWebImageOperation> subOperation = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *data, NSError *error, BOOL finished) {
            if (weakOperation.isCancelled) {
              // 操作被取消,则不做任务事情
            }
            else if (error) {
              // 假设出错。则调用完毕回调,并将url放入下载挫败url数组中
              ...
            }
            else {
              BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly);
              if (options & SDWebImageRefreshCached && image && !downloadedImage) {
                // Image refresh hit the NSURLCache cache, do not call the completion block
              }
              else if (downloadedImage && (!downloadedImage.images || (options & SDWebImageTransformAnimatedImage)) && [self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)]) {
                // 在全局队列中并行处理图片的缓存
                // 首先对图片做个转换操作,该操作是代理对象实现的
                // 然后对图片做缓存处理
                dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
                  UIImage *transformedImage = [self.delegate imageManager:self transformDownloadedImage:downloadedImage withURL:url];
                  if (transformedImage && finished) {
                    BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage];
                    [self.imageCache storeImage:transformedImage recalculateFromImage:imageWasTransformed imageData:data forKey:key toDisk:cacheOnDisk];
                  }
                  ...
                });
              }
              else {
                if (downloadedImage && finished) {
                  [self.imageCache storeImage:downloadedImage recalculateFromImage:NO imageData:data forKey:key toDisk:cacheOnDisk];
                }
                ...
              }
            }
            // 下载完毕并缓存后,将操作从队列中移除
            if (finished) {
              @synchronized (self.runningOperations) {
                [self.runningOperations removeObject:operation];
              }
            }
          }];
          // 设置取消回调
          operation.cancelBlock = ^{
            [subOperation cancel];
            @synchronized (self.runningOperations) {
              [self.runningOperations removeObject:weakOperation];
            }
          };
        }
        else if (image) {
          ...
        }
        else {
          ...
        }
      }];
      return operation;
    }
    对于这种方法,我们没有做过多的解释。其主要就是下载图片并依据操作选项来缓存图片。

    上面这个下载方法中的操作选项參数是由枚举SDWebImageOptions来定义的,这个操作中的一些选项是与SDWebImageDownloaderOptions中的选项相应的。我们来看看这个SDWebImageOptions选项都有哪些:

    typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {
      // 默认情况下,当URL下载失败时,URL会被列入黑名单,导致库不会再去重试,该标记用于禁用黑名单
      SDWebImageRetryFailed = 1 << 0,
      // 默认情况下,图片下载開始于UI交互。该标记禁用这一特性,这样下载延迟到UIScrollView减速时
      SDWebImageLowPriority = 1 << 1,
      // 该标记禁用磁盘缓存
      SDWebImageCacheMemoryOnly = 1 << 2,
      // 该标记启用渐进式下载。图片在下载过程中是渐渐显示的。如同浏览器一下。
      // 默认情况下。图像在下载完毕后一次性显示
      SDWebImageProgressiveDownload = 1 << 3,
      // 即使图片缓存了,也期望HTTP响应cache control,并在须要的情况下从远程刷新图片。

    // 磁盘缓存将被NSURLCache处理而不是SDWebImage。由于SDWebImage会导致轻微的性能下载。

    // 该标记帮助处理在同样请求URL后面改变的图片。

    假设缓存图片被刷新。则完毕block会使用缓存图片调用一次 // 然后再用终于图片调用一次 SDWebImageRefreshCached = 1 << 4, // 在iOS 4+系统中,当程序进入后台后继续下载图片。这将要求系统给予额外的时间让请求完毕 // 假设后台任务超时,则操作被取消 SDWebImageContinueInBackground = 1 << 5, // 通过设置NSMutableURLRequest.HTTPShouldHandleCookies = YES;来处理存储在NSHTTPCookieStore中的cookie SDWebImageHandleCookies = 1 << 6, // 同意不受信任的SSL认证 SDWebImageAllowInvalidSSLCertificates = 1 << 7, // 默认情况下。图片下载按入队的顺序来运行。该标记将其移到队列的前面, // 以便图片能马上下载而不是等到当前队列被载入 SDWebImageHighPriority = 1 << 8, // 默认情况下,占位图片在载入图片的同一时候被载入。该标记延迟占位图片的载入直到图片已以被载入完毕 SDWebImageDelayPlaceholder = 1 << 9, // 通常我们不调用动绘图片的transformDownloadedImage代理方法,由于大多数转换代码能够管理它。 // 使用这个票房则不不论什么情况下都进行转换。 SDWebImageTransformAnimatedImage = 1 << 10, };

    大家在看-downloadImageWithURL:options:progress:completed:,能够看到两个SDWebImageOptions与SDWebImageDownloaderOptions中的选项是怎样相应起来的,在此不多做解释。


    视图扩展

    我在使用SDWebImage的时候,使用得最多的是UIImageView+WebCache中的针对UIImageView的扩展方法。这些扩展方法将UIImageView与WebCache集成在一起。来让UIImageView对象拥有异步下载和缓存远程图片的能力。

    当中最核心的方法是-sd_setImageWithURL:placeholderImage:options:progress:completed:,其使用SDWebImageManager单例对象下载并缓存图片,完毕后将图片赋值给UIImageView对象的image属性,以使图片显示出来,其详细实现例如以下:

    - (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock {
      ...
      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) {
          if (!wself) return;
          dispatch_main_sync_safe(^{
            if (!wself) return;
            // 图片下载完后显示图片
            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 {
        ...
      }
    }

    除了扩展UIImageView之外,SDWebImage还扩展了UIView、UIButton、MKAnnotationView等视图类。大家能够參考源代码。

    当然,假设不想使用这些扩展,则能够直接使用SDWebImageManager来下载图片,这也是非常OK的。


    技术点

    SDWebImage的主要任务就是图片的下载和缓存。为了支持这些操作,它主要使用了下面知识点:

    1. dispatch_barrier_sync函数:该方法用于对操作设置屏幕,确保在运行完任务后才会运行兴许操作。该方法经常使用于确保类的线程安全性操作。

    2. NSMutableURLRequest:用于创建一个网络请求对象,我们能够依据须要来配置请求报头等信息。

    3. NSOperation及NSOperationQueue:操作队列是Objective-C中一种高级的并发处理方法,如今它是基于GCD来实现的。相对于GCD来说,操作队列的长处是能够取消在任务处理队列中的任务。另外在管理操作间的依赖关系方面也easy一些。对SDWebImage中我们就看到了怎样使用依赖将下载顺序设置成后进先出的顺序。

    4. NSURLConnection:用于网络请求及响应处理。在iOS7.0后,苹果推出了一套新的网络请求接口。即NSURLSession类。

    5. 开启一个后台任务。

    6. NSCache类:一个类似于集合的容器。它存储key-value对,这一点类似于NSDictionary类。我们通经常使用使用缓存来暂时存储短时间使用但创建昂贵的对象。重用这些对象能够优化性能,由于它们的值不须要又一次计算。

      另外一方面。这些对象对于程序来说不是紧要的,在内存紧张时会被丢弃。

    7. 清理缓存图片的策略:特别是最大缓存空间大小的设置。

      假设全部缓存文件的总大小超过这一大小。则会依照文件最后改动时间的逆序。以每次一半的递归来移除那些过早的文件。直到缓存的实际大小小于我们设置的最大使用空间。

    8. 对图片的解压缩操作:这一操作能够查看SDWebImageDecoder.m中+decodedImageWithImage方法的实现。
    9. 对GIF图片的处理
    10. 对WebP图片的处理


    參考:
    1. http://southpeak.github.io/blog/2015/02/07/yuan-ma-pian-:sdwebimage/?utm_source=tuicool&utm_medium=referral
    2. http://blog.sina.com.cn/s/blog_8988732e0101af25.html
    3. http://www.jianshu.com/p/c07df06c60be
    4. http://www.zuimoban.com/jiaocheng/ios/2016/0310/6794.html

  • 相关阅读:
    模糊搜索工具fzf的安装和使用
    ubuntu状态栏位置调整
    毕业相关事项
    vimium拓展程序使用技巧
    python文件生成exe可执行文件
    解决pycharm无法识别PyQt4.QtCore的问题
    pyqt4安装以及pycharm下环境配置
    动态规划——最小编辑代价
    深度学习绘图工具
    Maven Spring JUnit 在Maven Clean Install时报
  • 原文地址:https://www.cnblogs.com/zhchoutai/p/8458419.html
Copyright © 2011-2022 走看看