1、ImageCache

  • 使用内存缓存方式:

  • 使用沙盒缓存方式:

  • 使用网络图片第三方库方式:

    • SDWebImage:

      • iOS 中著名的网络图片处理框架

      • 包含的功能:图片下载、图片缓存、下载进度监听、gif 处理等等

      • 用法极其简单,功能十分强大,大大提高了网络图片的处理效率

      • 国内超过 90% 的 iOS 项目都有它的影子

      • 1、图片文件缓存的时间有多长?

        • 1 周
        	_maxCacheAge = kDefaultCacheMaxCacheAge;                                    // - (id)initWithNamespace: diskCacheDirectory:
        
        	static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7;         // 1 week   SDImageCache.m
      • 2、SDWebImage 的内存缓存是用什么实现的?

        • NSCache
      • 3、SDWebImage 的最大并发数是多少?

        • 是程序固定死了,可以通过属性进行调整!
        	_downloadQueue.maxConcurrentOperationCount = 6;                             // SDWebImageDownloader.m   - (id)init
      • 4、网络访问超时时长

        • iOS 开发中,默认的访问时长是 1 分钟,SDWebImage 中是 15 秒。
        	_downloadTimeout = 15.0;                                                    // SDWebImageDownloader.m   - (id)init
      • 5、SDWebImage 支持动图吗?GIF

        • 支持
        	#import <ImageIO/ImageIO.h>                                                 // UIImage+GIF.m
        [UIImage animatedImageWithImages:images duration:duration];
      • 6、SDWebImage 是如何区分不同格式的图像的

        • 根据图像数据第一个字节来判断的!
        	// NSData+ImageContentType.m    + (NSString *)sd_contentTypeForImageData:
        
        	PNG:0x89 image/png ,压缩比没有 JPG 高,但是无损压缩,解压缩性能高,苹果推荐的图像格式!
        JPG:0xFF image/jpeg,压缩比最高的一种图片格式,有损压缩!最多使用的场景,照相机!解压缩的性能不好!
        GIF:0x47 image/gif ,序列桢动图,特点:只支持 256 种颜色!最流行的时候在 1998~1999,有专利的!
      • 7、SDWebImage 缓存图片的名称是怎么确定的!

        • md5

        • 如果单纯使用 文件名保存,重名的几率很高!

        • 使用 MD5 的散列函数!对完整的 URL 进行 md5,结果是一个 32 个字符长度的字符串!

      • 8、SDWebImage 的内存警告是如何处理的!

        • 利用通知中心观察

        • UIApplicationDidReceiveMemoryWarningNotification 接收到内存警告的通知

        • 执行 clearMemory 方法,清理内存缓存!

        • UIApplicationWillTerminateNotification 接收到应用程序将要终止通知

        • 执行 cleanDisk 方法,清理磁盘缓存!

        • UIApplicationDidEnterBackgroundNotification 接收到应用程序进入后台通知

        • 执行 backgroundCleanDisk 方法,后台清理磁盘!

        • 通过以上通知监听,能够保证缓存文件的大小始终在控制范围之内!

        • clearDisk 清空磁盘缓存,将所有缓存目录中的文件,全部删除! 实际工作,将缓存目录直接删除,再次创建一个同名空目录!

      • bug:

        • SDWebImage 中,一旦内存警告,清理了内存之后,之后所有的图片都是从沙盒加载的。

        • 原因:NSCache 中一旦调用了 removeAllObjects,就无法给 cache 添加对象。关于 NSCache 的内存管理,交给他自己就行!

2、自定义内存缓存方式

  • Objective-C

    • AppInfoModel.h

      	@interface AppInfoModel : NSObject
      
      	/// 标题名称
      @property (nonatomic, strong) NSString *name; /// 下载数量
      @property (nonatomic, strong) NSString *download; /// 图片地址
      @property (nonatomic, strong) NSString *icon; /// 声明工厂方法,数据源初始化方法
      + (instancetype)appInfoModelWithDict:(NSDictionary *)dict; @end
    • AppInfoModel.m

      	// 实现工厂方法
      + (instancetype)appInfoModelWithDict:(NSDictionary *)dict { id obj = [[self alloc] init]; [obj setValuesForKeysWithDictionary:dict]; return obj;
      }
    • AppInfoCell.h

      	@interface AppInfoCell : UITableViewCell
      
      	@property (nonatomic, weak) IBOutlet UILabel *nameLabel;
      @property (nonatomic, weak) IBOutlet UILabel *downloadLabel;
      @property (nonatomic, weak) IBOutlet UIImageView *iconImageView; @end
    • ViewController.m

      	/// 表格数据源
      @property (nonatomic, strong) NSArray *dataSourceArray; /// 图片下载队列
      @property (nonatomic, strong) NSOperationQueue *downloadQueue; /// 下载缓冲池
      @property (nonatomic, strong) NSMutableDictionary *downloadQueueCache; /// 图片缓冲池
      @property (nonatomic, strong) NSMutableDictionary *imageCache; /// 图片下载地址黑名单
      @property (nonatomic, retain) NSMutableArray *urlBlackList; // 懒加载 - (NSArray *)dataSourceArray {
      if (_dataSourceArray == nil) {
      NSArray *array = [NSArray arrayWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"apps.plist" withExtension:nil]]; NSMutableArray *arrayM = [NSMutableArray arrayWithCapacity:array.count];
      [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
      [arrayM addObject:[AppInfoModel appInfoModelWithDict:obj]];
      }];
      _dataSourceArray = [arrayM copy];
      }
      return _dataSourceArray;
      } - (NSOperationQueue *)downloadQueue {
      if (_downloadQueue == nil) {
      _downloadQueue = [[NSOperationQueue alloc] init];
      }
      return _downloadQueue;
      } - (NSMutableDictionary *)downloadQueueCache {
      if (_downloadQueueCache == nil) {
      _downloadQueueCache = [[NSMutableDictionary alloc] init];
      }
      return _downloadQueueCache;
      } - (NSMutableDictionary *)imageCache {
      if (_imageCache == nil) {
      _imageCache = [[NSMutableDictionary alloc] init];
      }
      return _imageCache;
      } - (NSMutableArray *)urlBlackList {
      if (_urlBlackList == nil) {
      _urlBlackList = [[NSMutableArray alloc] init];
      }
      return _urlBlackList;
      } // 表格视图数据源方法 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
      return self.dataSourceArray.count;
      } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { AppInfoCell *cell = [tableView dequeueReusableCellWithIdentifier:@"AppCell" forIndexPath:indexPath]; AppInfoModel *dataModel = self.dataSourceArray[indexPath.row]; cell.nameLabel.text = dataModel.name;
      cell.downloadLabel.text = dataModel.download; // 判断图片缓存池中是否有相应的图片
      if (self.imageCache[dataModel.icon] != nil) { // 从缓存池中取出图片显示在 Cell 上
      cell.iconImageView.image = self.imageCache[dataModel.icon];
      } else { // 从网络异步下载图片
      [self downloadImageWithIndexPath:indexPath];
      } return cell;
      } - (void)downloadImageWithIndexPath:(NSIndexPath *)indexPath { AppInfoModel *dataModel = self.dataSourceArray[indexPath.row]; // 判断下载缓冲池中是否存在当前下载操作
      if (self.downloadQueueCache[dataModel.icon] != nil) {
      return;
      } // 判断图片地址是否在黑名单中
      if ([self.urlBlackList containsObject:dataModel.icon]) {
      return;
      } // 创建异步下载图片操作
      NSBlockOperation *downloadOperation = [NSBlockOperation blockOperationWithBlock:^{ UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:dataModel.icon]]]; // 下载完成从下载缓冲池中删除当前下载操作
      [self.downloadQueueCache removeObjectForKey:dataModel.icon]; // 添加黑名单记录
      if (image == nil && ![self.urlBlackList containsObject:dataModel.icon]) {
      [self.urlBlackList addObject:dataModel.icon];
      } // 主线程跟新 UI
      [[NSOperationQueue mainQueue] addOperationWithBlock:^{ if (image != nil) { // 将下载完成的图片保存到图片缓冲池中
      [self.imageCache setObject:image forKey:dataModel.icon]; [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
      }
      }];
      }]; // 将当前下载添加到下载缓冲池中
      [self.downloadQueueCache setObject:downloadOperation forKey:dataModel.icon]; // 开始异步下载图片
      [self.downloadQueue addOperation:downloadOperation];
      } // 内存警告
      /*
      日常上课通常就直接删除,但是在工作后,必须要处理!不处理后果很严重,第一次内存警告如果不处理,就没有第二次的机会了,就直接被闪退了
      */ - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // 清理缓冲池
      [self.downloadQueueCache removeAllObjects];
      [self.imageCache removeAllObjects]; // 取消下载操作,等用户再滚动表格,调用数据源方法,又能够自动下载
      [self.downloadQueue cancelAllOperations];
      }
  • Swift

    • AppInfo.swift

      	class AppInfo: NSObject {
      
          	var name:String!
      var icon:String!
      var download:String! class func AppInfoWithDict(dict:[String:AnyObject]) -> AnyObject {
      let obj:AnyObject = self.init() obj.setValuesForKeysWithDictionary(dict) return obj
      } required override init() {
      super.init()
      }
      }
    • AppInfoCell.swift

      	class AppInfoCell: UITableViewCell {
      
          	@IBOutlet weak var iconView: UIImageView!
      @IBOutlet weak var nameLabel: UILabel!
      @IBOutlet weak var downLabel: UILabel!
      }
    • ViewController.swift

      	// 懒加载
      
          	// 数据源
      lazy var dataSourceArray:[AnyObject] = {
      var dataArray:[AnyObject] = Array() let array = NSArray(contentsOfURL: NSBundle.mainBundle().URLForResource("apps.plist", withExtension: nil)!)
      array!.enumerateObjectsUsingBlock { (obj:AnyObject, idx:Int, stop:UnsafeMutablePointer<ObjCBool>) in
      dataArray.append(AppInfoModel.AppInfoWithDict(obj as! [String : AnyObject]))
      }
      return dataArray
      }() // 下载队列
      lazy var downloadQueue:NSOperationQueue = {
      var tmp:NSOperationQueue = NSOperationQueue()
      return tmp
      }() // 下载缓冲池
      lazy var downloadQueueCache:[String:NSBlockOperation] = {
      var tmp:[String:NSBlockOperation] = Dictionary()
      return tmp
      }() // 图片缓冲池
      lazy var imageCache:[String:UIImage] = {
      var tmp:[String:UIImage] = Dictionary()
      return tmp
      }() // 图片下载地址黑名单
      lazy var urlBlackList:[String] = {
      var tmp:[String] = Array()
      return tmp
      }() // 表格视图数据源方法 override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
      return self.dataSourceArray.count
      } override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell:AppInfoCell = tableView.dequeueReusableCellWithIdentifier("AppCell", forIndexPath: indexPath) as! AppInfoCell let dataModel = self.dataSourceArray[indexPath.row] as! AppInfoModel cell.nameLabel.text = dataModel.name
      cell.downLabel.text = dataModel.download // 判断图片缓存池中是否有相应的图片
      if self.imageCache[dataModel.icon] != nil { // 从缓存池中取出图片显示在 Cell 上
      cell.iconView.image = self.imageCache[dataModel.icon]
      } else { // 从网络异步下载图片
      self.downloadImageWithIndexPath(indexPath)
      }
      return cell
      } func downloadImageWithIndexPath(indexPath:NSIndexPath) { let dataModel = self.dataSourceArray[indexPath.row] as! AppInfoModel // 判断下载缓冲池中是否存在当前下载操作
      if self.downloadQueueCache[dataModel.icon] != nil {
      return
      } // 判断图片地址是否在黑名单中
      if self.urlBlackList.contains(dataModel.icon) {
      return
      } // 创建异步下载图片操作
      let downloadOperation = NSBlockOperation { let image:UIImage? = UIImage(data: NSData(contentsOfURL: NSURL(string: dataModel.icon)!)!) // 下载完成从下载缓冲池中删除当前下载操作
      self.downloadQueueCache.removeValueForKey(dataModel.icon) // 添加黑名单记录
      if image == nil && !self.urlBlackList.contains(dataModel.icon) { self.urlBlackList.append(dataModel.icon)
      } // 主线程跟新 UI
      NSOperationQueue.mainQueue().addOperationWithBlock({ if image != nil { // 将下载完成的图片保存到图片缓冲池中
      self.imageCache[dataModel.icon] = image self.tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
      }
      })
      } // 将当前下载添加到下载缓冲池中
      self.downloadQueueCache[dataModel.icon] = downloadOperation // 开始异步下载图片
      self.downloadQueue.addOperation(downloadOperation)
      } // 内存警告 override func didReceiveMemoryWarning() {
      super.didReceiveMemoryWarning() self.downloadQueueCache.removeAll()
      self.imageCache.removeAll() self.downloadQueue.cancelAllOperations()
      }

3、自定义沙盒缓存方式

  • Objective-C

    • AppInfoModel.h

    • AppInfoModel.m

    • AppInfoCell.h

      • 与上边一样
    • NSString+BundlePath.h

      	///  拼接缓存目录
      - (NSString *)appendCachePath;
    • NSString+BundlePath.m

      	- (NSString *)appendCachePath {
      NSString *dir = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).lastObject;
      return [dir stringByAppendingPathComponent:self.lastPathComponent];
      }
    • ViewController.m

      	// 懒加载
      
      		与上边一样
      
      	// 表格视图数据源方法
      
          	- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
      return self.dataSourceArray.count;
      } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { AppInfoCell *cell = [tableView dequeueReusableCellWithIdentifier:@"AppCell" forIndexPath:indexPath]; AppInfoModel *dataModel = self.dataSourceArray[indexPath.row]; cell.nameLabel.text = dataModel.name;
      cell.downloadLabel.text = dataModel.download; // 判断图片缓存池中是否有相应的图片
      if (self.imageCache[dataModel.icon] != nil) { // 从缓存池中取出图片显示在 Cell 上
      cell.iconImageView.image = self.imageCache[dataModel.icon];
      } else { // 从沙盒加载图片
      UIImage *image = [UIImage imageWithContentsOfFile:[dataModel.icon appendCachePath]]; if (image != nil) { cell.iconImageView.image = image;
      self.imageCache[dataModel.icon] = image; } else { // 从网络异步下载图片
      [self downloadImageWithIndexPath:indexPath];
      }
      } return cell;
      } - (void)downloadImageWithIndexPath:(NSIndexPath *)indexPath { AppInfoModel *dataModel = self.dataSourceArray[indexPath.row]; // 判断下载缓冲池中是否存在当前下载操作
      if (self.downloadQueueCache[dataModel.icon] != nil) {
      return;
      } // 判断图片地址是否在黑名单中
      if ([self.urlBlackList containsObject:dataModel.icon]) { return;
      } // 创建异步下载图片操作
      NSBlockOperation *downloadOperation = [NSBlockOperation blockOperationWithBlock:^{ NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:dataModel.icon]];
      UIImage *image = [UIImage imageWithData:imageData]; // 下载完成从下载缓冲池中删除当前下载操作
      [self.downloadQueueCache removeObjectForKey:dataModel.icon]; // 添加黑名单记录
      if (image == nil && ![self.urlBlackList containsObject:dataModel.icon]) {
      [self.urlBlackList addObject:dataModel.icon];
      } if (image != nil) { // 将图像写入沙盒
      [imageData writeToFile:[dataModel.icon appendCachePath] atomically:YES];
      } // 主线程跟新 UI
      [[NSOperationQueue mainQueue] addOperationWithBlock:^{ if (image != nil) { // 将下载完成的图片保存到图片缓冲池中
      [self.imageCache setObject:image forKey:dataModel.icon]; [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
      }
      }];
      }]; // 将当前下载添加到下载缓冲池中
      [self.downloadQueueCache setObject:downloadOperation forKey:dataModel.icon]; // 开始异步下载图片
      [self.downloadQueue addOperation:downloadOperation];
      } // 内存警告 与上边一样
  • Swift

    • AppInfo.swift

    • AppInfoCell.swift

      • 与上边一样
    • String+BundlePath.swift

      	public func appendCachePath() -> String? {
      let dir:String? = NSSearchPathForDirectoriesInDomains(.CachesDirectory, .UserDomainMask, true).last
      if (dir != nil) {
      return dir! + "/" + (self as NSString).lastPathComponent
      } else {
      return nil
      }
      }
    • ViewController.swift

      	// 懒加载
      
      		与上边一样
      
      	// 表格视图数据源方法
      
          	override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
      return self.dataSourceArray.count
      } override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell:AppInfoCell = tableView.dequeueReusableCellWithIdentifier("AppCell", forIndexPath: indexPath) as! AppInfoCell let dataModel = self.dataSourceArray[indexPath.row] as! AppInfoModel cell.nameLabel.text = dataModel.name
      cell.downLabel.text = dataModel.download // 判断图片缓存池中是否有相应的图片
      if self.imageCache[dataModel.icon] != nil { // 从缓存池中取出图片显示在 Cell 上
      cell.iconView.image = self.imageCache[dataModel.icon] } else { // 从沙盒加载图片
      let image = UIImage(contentsOfFile: dataModel.icon.appendCachePath()!) if (image != nil) { cell.iconView.image = image
      self.imageCache[dataModel.icon] = image } else { // 从网络异步下载图片
      self.downloadImageWithIndexPath(indexPath)
      }
      } return cell
      } func downloadImageWithIndexPath(indexPath:NSIndexPath) { let dataModel = self.dataSourceArray[indexPath.row] as! AppInfoModel // 判断下载缓冲池中是否存在当前下载操作
      if self.downloadQueueCache[dataModel.icon] != nil { return
      } // 判断图片地址是否在黑名单中
      if self.urlBlackList.contains(dataModel.icon) { return
      } // 创建异步下载图片操作
      let downloadOperation = NSBlockOperation { let imageData:NSData? = NSData(contentsOfURL: NSURL(string: dataModel.icon)!) let image:UIImage? = UIImage(data: imageData!) // 下载完成从下载缓冲池中删除当前下载操作
      self.downloadQueueCache.removeValueForKey(dataModel.icon) // 添加黑名单记录
      if image == nil && !self.urlBlackList.contains(dataModel.icon) { self.urlBlackList.append(dataModel.icon)
      } if image != nil { // 将图像写入沙盒
      imageData!.writeToFile(dataModel.icon.appendCachePath()!, atomically: true) print(dataModel.icon.appendCachePath()!)
      } // 主线程跟新 UI
      NSOperationQueue.mainQueue().addOperationWithBlock({ if image != nil { // 将下载完成的图片保存到图片缓冲池中
      self.imageCache[dataModel.icon] = image self.tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
      }
      })
      } // 将当前下载添加到下载缓冲池中
      self.downloadQueueCache[dataModel.icon] = downloadOperation // 开始异步下载图片
      self.downloadQueue.addOperation(downloadOperation)
      } // 内存警告 与上边一样

4、仿 SDWebImage 缓存方式

  • Objective-C

    • AppInfoModel.h

      	@interface AppInfoModel : NSObject
      
      	/// 标题名称
      @property (nonatomic, strong) NSString *name; /// 下载数量
      @property (nonatomic, strong) NSString *download; /// 图片地址
      @property (nonatomic, strong) NSString *icon; /// 从 Plist 加载 AppInfo
      + (NSArray *)loadPList; @end
    • AppInfoModel.m

      	+ (NSArray *)loadPList {
      
          	NSArray *array = [NSArray arrayWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"apps.plist" withExtension:nil]];
      
          	NSMutableArray *plist = [NSMutableArray arrayWithCapacity:array.count];
      [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { id model = [[self alloc] init];
      [model setValuesForKeysWithDictionary:obj]; [plist addObject:model];
      }]; return plist;
      }
    • AppInfoCell.h

      	@interface AppInfoCell : UITableViewCell
      
      	@property (nonatomic, weak) IBOutlet UILabel *nameLabel;
      @property (nonatomic, weak) IBOutlet UILabel *downloadLabel;
      @property (nonatomic, weak) IBOutlet UIImageView *iconImageView; @end
    • NSString+BundlePath.h

      	///  拼接缓存目录
      - (NSString *)appendCachePath;
    • NSString+BundlePath.m

      	- (NSString *)appendCachePath {
      NSString *dir = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).lastObject;
      return [dir stringByAppendingPathComponent:self.lastPathComponent];
      }
    • WebImageOperation.h

      	@interface WebImageOperation : NSOperation
      
      	///  实例化 web 图像操作
      + (instancetype)webImageOperationWithURLString:(NSString *)urlString completion:(void (^)(UIImage *image))completion; @end
    • WebImageOperation.m

      	/// 下载图片的 URL
      @property (nonatomic, copy) NSString *urlStr; /// 下载完成的回调
      @property (nonatomic, copy) void (^completion) (UIImage *image); + (instancetype)webImageOperationWithURLString:(NSString *)urlString completion:(void (^)(UIImage *))completion { WebImageOperation *imageOperation = [[self alloc] init]; imageOperation.urlStr= urlString;
      imageOperation.completion = completion; return imageOperation;
      } // 操作加入队列后会自动执行该方法
      - (void)main {
      @autoreleasepool { if (self.isCancelled) return; NSURL *url = [NSURL URLWithString:self.urlStr];
      NSData *data = [NSData dataWithContentsOfURL:url]; if (self.isCancelled) return; if (data != nil) {
      [data writeToFile:self.urlStr.appendCachePath atomically:YES];
      } if (self.isCancelled) return; if (self.completion && data != nil) { [[NSOperationQueue mainQueue] addOperationWithBlock:^{ self.completion([UIImage imageWithData:data]);
      }];
      }
      }
      }
    • WebImageManager.h

      	//  负责所有网络图像的下载操作以及缓存管理!
      @interface WebImageManager : NSObject /// 全局单例访问入口
      + (instancetype)sharedManager; /// 下载网络图像
      - (void)downloadImage:(NSString *)urlString completion:(void (^) (UIImage *image))completion; /// 取消 urlString 对应的下载操作
      - (void)cancelDownload:(NSString *)urlString; @end
    • WebImageManager.m

      	/// 下载队列
      @property (nonatomic, strong) NSOperationQueue *downloadQueue; /// 下载操作缓冲池
      @property (nonatomic, strong) NSMutableDictionary *downloadQueueCache; /// 图片缓冲池
      @property (nonatomic, strong) NSMutableDictionary *imageCache; // 下载管理器 // 实例化下载管理器
      + (instancetype)sharedManager { static id instance; static dispatch_once_t onceToken;
      dispatch_once(&onceToken, ^{
      instance = [[self alloc] init];
      });
      return instance;
      } // 下载操作 - (void)downloadImage:(NSString *)urlString completion:(void (^)(UIImage *))completion { // 判断缓存中是否存在图像
      if ([self checkCacheWithURLString:urlString]) { if (completion != nil) { // 直接回调,传递给调用方图像
      completion(self.imageCache[urlString]);
      } return;
      } // 判断缓冲池中是否存在下载操作
      if (self.downloadQueueCache[urlString] != nil) { return;
      } WebImageOperation *downloadOperation = [WebImageOperation webImageOperationWithURLString:urlString
      completion:^(UIImage *image) { // 下载完成从操作缓冲池中移除操作
      [self.downloadQueueCache removeObjectForKey:urlString]; // 下载完成添加到图片缓冲池中
      [self.imageCache setObject:image forKey:urlString]; if (completion != nil) {
      completion(image);
      }
      }]; // 将操作添加到缓冲池
      [self.downloadQueueCache setObject:downloadOperation forKey:urlString]; // 将操作添加到队列
      [self.downloadQueue addOperation:downloadOperation];
      } // 取消 urlString 对应的下载操作
      - (void)cancelDownload:(NSString *)urlString { // 从缓冲池拿到下载操作
      WebImageOperation *downloadOperation = self.downloadQueueCache[urlString]; if (downloadOperation != nil) { // 取消操作
      [downloadOperation cancel]; // 从缓冲池中删除操作
      [self.downloadQueueCache removeObjectForKey:urlString];
      }
      } // 判断缓存中是否存在图像
      - (BOOL)checkCacheWithURLString:(NSString *)urlString { // 判断图片缓冲池中是否存在图像
      if (self.imageCache[urlString] != nil) {
      return YES;
      } UIImage *image = [UIImage imageWithContentsOfFile:[urlString appendCachePath]]; // 判断沙盒中是否存在图像
      if (image != nil) { [self.imageCache setObject:image forKey:urlString]; return YES;
      } return NO;
      } // 懒加载 - (NSOperationQueue *)downloadQueue {
      if (_downloadQueue == nil) {
      _downloadQueue = [[NSOperationQueue alloc] init];
      }
      return _downloadQueue;
      } - (NSMutableDictionary *)downloadQueueCache {
      if (_downloadQueueCache == nil) {
      _downloadQueueCache = [[NSMutableDictionary alloc] init];
      }
      return _downloadQueueCache;
      } - (NSMutableDictionary *)imageCache {
      if (_imageCache == nil) {
      _imageCache = [[NSMutableDictionary alloc] init];
      }
      return _imageCache;
      }
    • UIImageView+WebImageView.h

      	@interface UIImageView (WebImageView)
      
      	/// 设置 Web 图像 URL,自动加载图像
      - (void)setWebImageWithURL:(NSString *)urlString; @end
    • UIImageView+WebImageView.m

      	#import <objc/runtime.h>
      
      	// 下载图片的 url
      @property (nonatomic, copy) NSString *urlStr; - (void)setWebImageWithURL:(NSString *)urlString { // 屏蔽快速滑动重复添加下载
      if ([self.urlStr isEqualToString:urlString]) { return;
      } // 暂停之前的操作
      if (self.urlStr != nil && ![self.urlStr isEqualToString:urlString]) { [[WebImageManager sharedManager] cancelDownload:self.urlStr]; // 如果 ImageView 之前有图像-清空图像
      self.image = nil;
      } // 记录新的 url
      self.urlStr = urlString; __weak typeof(self) weakSelf = self; // 下载网络图片
      [[WebImageManager sharedManager] downloadImage:self.urlStr completion:^(UIImage *image) {
      weakSelf.image = image;
      }];
      } // 向分类添加属性 // 运行时的关联对象,动态添加属性
      const void *URLStrKey = "URLStrKey"; - (void)setUrlStr:(NSString *)urlString {
      objc_setAssociatedObject(self, URLStrKey, urlString, OBJC_ASSOCIATION_COPY_NONATOMIC);
      } - (NSString *)urlStr {
      return objc_getAssociatedObject(self, URLStrKey);
      }
    • ViewController.m

      	/// 表格数据源
      @property (nonatomic, strong) NSArray *dataSourceArray; // 懒加载 - (NSArray *)dataSourceArray {
      if (_dataSourceArray == nil) {
      _dataSourceArray = [AppInfoModel loadPList];
      }
      return _dataSourceArray;
      } // 表格视图数据源方法 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
      return self.dataSourceArray.count;
      } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { AppInfoCell *cell = [tableView dequeueReusableCellWithIdentifier:@"AppCell" forIndexPath:indexPath]; AppInfoModel *dataModel = self.dataSourceArray[indexPath.row]; cell.nameLabel.text = dataModel.name;
      cell.downloadLabel.text = dataModel.download; [cell.iconImageView setWebImageWithURL:dataModel.icon]; return cell;
      }
  • Swift

    • AppInfoModel.swift

      	class AppInfoModel: NSObject
      
      	var name:String!
      var icon:String!
      var download:String! class func loadPList() -> [AnyObject] {
      let array = NSArray(contentsOfURL: NSBundle.mainBundle().URLForResource("apps.plist", withExtension: nil)!) var plist:[AnyObject] = Array()
      array!.enumerateObjectsUsingBlock { (obj:AnyObject, idx:Int, stop:UnsafeMutablePointer<ObjCBool>) in let model:AnyObject = self.init() model.setValuesForKeysWithDictionary(obj as! [String : AnyObject]) plist.append(model)
      }
      return plist
      } class func AppInfoWithDict(dict:[String:AnyObject]) -> AnyObject {
      let obj:AnyObject = self.init() obj.setValuesForKeysWithDictionary(dict) return obj
      } required override init() {
      super.init()
      }
    • AppInfoCell.swift

      	@IBOutlet weak var iconView: UIImageView!
      @IBOutlet weak var nameLabel: UILabel!
      @IBOutlet weak var downLabel: UILabel!
    • String+BundlePath.swift

      	///  拼接缓存目录
      public func appendCachePath() -> String? {
      let dir:String? = NSSearchPathForDirectoriesInDomains(.CachesDirectory, .UserDomainMask, true).last
      if (dir != nil) {
      return dir! + "/" + (self as NSString).lastPathComponent
      } else {
      return nil
      }
      }
    • WebImageOperation.swift

      	class WebImageOperation: NSOperation
      
      	/// 下载图片的 URL
      var urlStr:String! /// 下载完成的回调
      var completion:((image:UIImage) -> Void)! class func webImageOperationWithURLString(urlString:String, completion:((image:UIImage) -> Void)) -> WebImageOperation { let imageOperation:WebImageOperation = WebImageOperation() imageOperation.urlStr = urlString
      imageOperation.completion = completion return imageOperation
      } // 操作加入队列后会自动执行该方法
      override func main() { if self.cancelled == true {
      return
      } let url:NSURL = NSURL(string: self.urlStr)!
      let data:NSData? = NSData(contentsOfURL: url) if self.cancelled == true {
      return
      } if data != nil { data?.writeToFile(self.urlStr.appendCachePath()!, atomically: true)
      } if self.cancelled == true {
      return
      } if (self.completion != nil) && (data != nil) { NSOperationQueue.mainQueue().addOperationWithBlock({ self.completion(image: UIImage(data: data!)!)
      })
      }
      }
    • WebImageManager.swift

      	// 负责所有网络图像的下载操作以及缓存管理!
      class WebImageManager: NSObject // 下载队列
      lazy var downloadQueue:NSOperationQueue = { var tmp:NSOperationQueue = NSOperationQueue()
      return tmp
      }() // 下载缓冲池
      lazy var downloadQueueCache:[String:WebImageOperation] = { var tmp:[String:WebImageOperation] = Dictionary()
      return tmp
      }() // 图片缓冲池
      lazy var imageCache:[String:UIImage] = { var tmp:[String:UIImage] = Dictionary()
      return tmp
      }() // 下载管理器 static let sharedManager = WebImageManager()
      private override init() {} // 下载操作 func downloadImage(urlString:String, completion:((image:UIImage) -> Void)?) { // 判断缓存中是否存在图像
      if self.checkCacheWithURLString(urlString) == true { if completion != nil { // 直接回调,传递给调用方图像
      completion!(image: self.imageCache[urlString]!)
      } return
      } // 判断缓冲池中是否存在下载操作
      if self.downloadQueueCache[urlString] != nil { print("玩命下载中...稍安勿躁!") return
      } let downloadOperation:WebImageOperation = WebImageOperation.webImageOperationWithURLString(urlString) { (image) in // 下载完成从操作缓冲池中移除操作
      self.downloadQueueCache.removeValueForKey(urlString) // 下载完成添加到图片缓冲池中
      self.imageCache[urlString] = image if (completion != nil) {
      completion!(image: image);
      }
      } // 将操作添加到缓冲池
      self.downloadQueueCache[urlString] = downloadOperation // 将操作添加到队列
      self.downloadQueue.addOperation(downloadOperation)
      } // 取消 urlString 对应的下载操作
      func cancelDownload(urlString:String) { // 从缓冲池拿到下载操作
      let downloadOperation:WebImageOperation? = self.downloadQueueCache[urlString] if downloadOperation != nil { print("取消下载操作") // 取消操作
      downloadOperation!.cancel() // 从缓冲池中删除操作
      self.downloadQueueCache.removeValueForKey(urlString)
      }
      } // 判断缓存中是否存在图像
      func checkCacheWithURLString(urlString:String) -> Bool { // 判断图片缓冲池中是否存在图像
      if self.imageCache[urlString] != nil { print("从内存中加载...") return true
      } let image:UIImage? = UIImage(contentsOfFile: urlString.appendCachePath()!) // 判断沙盒中是否存在图像
      if image != nil { print("从沙盒中加载...") self.imageCache[urlString] = image return true
      } return false
      }
    • UIImageView+WebImageView.h

      	@interface UIImageView (WebImageView)
      
      	/// 设置 Web 图像 URL,自动加载图像
      - (void)setWebImageWithURL:(NSString *)urlString; @end
    • UIImageView+WebImageView.m

      	#import <objc/runtime.h>
      #import "SwiftImageCache-Swift.h" // 下载图片的 url
      @property (nonatomic, copy) NSString *urlStr; - (void)setWebImageWithURL:(NSString *)urlString { // 屏蔽快速滑动重复添加下载
      if ([self.urlStr isEqualToString:urlString]) { return;
      } // 暂停之前的操作
      if (self.urlStr != nil && ![self.urlStr isEqualToString:urlString]) { [[WebImageManager sharedManager] cancelDownload:self.urlStr]; // 如果 ImageView 之前有图像-清空图像
      self.image = nil;
      } // 记录新的 url
      self.urlStr = urlString; __weak typeof(self) weakSelf = self; // 下载网络图片
      [[WebImageManager sharedManager] downloadImage:self.urlStr completion:^(UIImage *image) {
      weakSelf.image = image;
      }];
      } // 向分类添加属性 // 运行时的关联对象,动态添加属性
      const void *URLStrKey = "URLStrKey"; - (void)setUrlStr:(NSString *)urlString {
      objc_setAssociatedObject(self, URLStrKey, urlString, OBJC_ASSOCIATION_COPY_NONATOMIC);
      } - (NSString *)urlStr {
      return objc_getAssociatedObject(self, URLStrKey);
      }
    • SwiftImageCache-Bridging-Header.h

      	#import "UIImageView+WebImageView.h"
    • ViewController.swift

      	// 懒加载
      
          	lazy var dataSourceArray:[AnyObject] = {
      var tmp:[AnyObject] = AppInfoModel.loadPList()
      return tmp
      }() // 表格视图数据源方法 override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
      return self.dataSourceArray.count
      } override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell:AppInfoCell = tableView.dequeueReusableCellWithIdentifier("AppCell", forIndexPath: indexPath) as! AppInfoCell let dataModel = self.dataSourceArray[indexPath.row] as! AppInfoModel cell.nameLabel.text = dataModel.name
      cell.downLabel.text = dataModel.download cell.iconView.setWebImageWithURL(dataModel.icon) return cell
      }

5、SDWebImage 缓存方式

  • Github 网址:https://github.com/rs/SDWebImage

  • SDWebImage 使用 ARC

  • Objective-C

    	// 添加第三方库文件
    SDWebImage // 包含头文件
    #import "UIImageView+WebCache.h"
    • AppInfoModel.h

      	@interface AppInfoModel : NSObject
      
      	/// 标题名称
      @property (nonatomic, strong) NSString *name; /// 下载数量
      @property (nonatomic, strong) NSString *download; /// 图片地址
      @property (nonatomic, strong) NSString *icon; /// 从 Plist 加载 AppInfo
      + (NSArray *)loadPList; @end
    • AppInfoModel.m

      	+ (NSArray *)loadPList {
      
          	NSArray *array = [NSArray arrayWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"apps.plist" withExtension:nil]];
      
          	NSMutableArray *plist = [NSMutableArray arrayWithCapacity:array.count];
      [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { id model = [[self alloc] init];
      [model setValuesForKeysWithDictionary:obj]; [plist addObject:model];
      }]; return plist;
      }
    • AppInfoCell.h

      	@interface AppInfoCell : UITableViewCell
      
      	@property (nonatomic, weak) IBOutlet UILabel *nameLabel;
      @property (nonatomic, weak) IBOutlet UILabel *downloadLabel;
      @property (nonatomic, weak) IBOutlet UIImageView *iconImageView; @end
    • ViewController.m

      	/// 表格数据源
      @property (nonatomic, strong) NSArray *dataSourceArray; // 懒加载 - (NSArray *)dataSourceArray {
      if (_dataSourceArray == nil) {
      _dataSourceArray = [AppInfoModel loadPList];
      }
      return _dataSourceArray;
      } // 表格视图数据源方法 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
      return self.dataSourceArray.count;
      } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { AppInfoCell *cell = [tableView dequeueReusableCellWithIdentifier:@"AppCell" forIndexPath:indexPath]; AppInfoModel *dataModel = self.dataSourceArray[indexPath.row]; cell.nameLabel.text = dataModel.name;
      cell.downloadLabel.text = dataModel.download; [cell.iconImageView sd_setImageWithURL:[NSURL URLWithString:dataModel.icon]]; return cell;
      }
  • Swift

    	// 添加第三方库文件
    SDWebImage // 创建桥接头文件,如
    SwiftImageCache-Bridging-Header.h // 在桥接头文件中添加头文件
    #import "UIImageView+WebCache.h"
    • AppInfoModel.swift

      	class AppInfoModel: NSObject	
      
      	var name:String!
      var icon:String!
      var download:String! class func loadPList() -> [AnyObject] {
      let array = NSArray(contentsOfURL: NSBundle.mainBundle().URLForResource("apps.plist", withExtension: nil)!) var plist:[AnyObject] = Array()
      array!.enumerateObjectsUsingBlock { (obj:AnyObject, idx:Int, stop:UnsafeMutablePointer<ObjCBool>) in let model:AnyObject = self.init() model.setValuesForKeysWithDictionary(obj as! [String : AnyObject]) plist.append(model)
      }
      return plist
      } class func AppInfoWithDict(dict:[String:AnyObject]) -> AnyObject {
      let obj:AnyObject = self.init() obj.setValuesForKeysWithDictionary(dict) return obj
      } required override init() {
      super.init()
      }
    • AppInfoCell.swift

      	class AppInfoCell: UITableViewCell	
      
      	@IBOutlet weak var iconView: UIImageView!
      @IBOutlet weak var nameLabel: UILabel!
      @IBOutlet weak var downLabel: UILabel!
    • ViewController.swift

      	// 懒加载
      
          	lazy var dataSourceArray:[AnyObject] = {
      var tmp:[AnyObject] = AppInfoModel.loadPList()
      return tmp
      }() // 表格视图数据源方法 override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
      return self.dataSourceArray.count
      } override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell:AppInfoCell = tableView.dequeueReusableCellWithIdentifier("AppCell", forIndexPath: indexPath) as! AppInfoCell let dataModel = self.dataSourceArray[indexPath.row] as! AppInfoModel cell.nameLabel.text = dataModel.name
      cell.downLabel.text = dataModel.download // 设置图片
      cell.iconView.sd_setImageWithURL(NSURL(string: dataModel.icon)) return cell
      }

iOS - ImageCache 网络图片缓存的更多相关文章

  1. iOS网络图片缓存详解

    在开发移动应用的时候比如Android,IOS,因为手机流量.网速.内存等这些因素,当我们的移动应用是针对互联网,并要频繁访问网络的话,对网络优化这块就显得尤为重要了. 比如某个应用要经常显示网络图片 ...

  2. iOS之 清理缓存

    作为一个开发者,对于缓存的清理也是理所应当的需要的.这次就简单的谈一下iOS中对于缓存的清理方法. 我们清理缓存通常是在这三种方式下进行的: (1)项目中的清理缓存按钮 (2)点击退出app按钮时清理 ...

  3. iOS中dyld缓存的实现原理是怎样的?

    在iOS开发中,为了提升系统的安全性,很多系统库文件都被打包到一个缓存的文件当中即dyld缓存,那大家对dyld缓存了解多少呢?今天小编将和大家分享的就是一位iOS大神对dyld缓存的使用分析,一起来 ...

  4. 玩转iOS开发 - 数据缓存

    Why Cache 有时候.对同一个URL请求多次,返回的数据可能都是一样的,比方server上的某张图片.不管下载多少次,返回的数据都是一样的. 上面的情况会造成下面问题 (1)用户流量的浪费 (2 ...

  5. iOS html5使用缓存并及时更新方案总结

    最近一段时间研究了一下H5在iOS移动端表现时使用缓存并可及时更新方案,总结如下: 一.使用Webview自带缓存机制 当我们使用webview加载html资源时的,本质上就是一个向服务器索取资源的h ...

  6. iOS开发网络缓存原理

    一.关于同一个URL的多次请求 有时候,对同一个URL请求多次,返回的数据可能都是一样的,比如服务器上的某张图片,无论下载多少次,返回的数据都是一样的. 上面的情况会造成以下问题 (1)用户流量的浪费 ...

  7. iOS 清理文件缓存

    本文摘自:<msp的昌伟哥哥-iOS开发-清理缓存功能的实现>摘下来的目的就是为了能够学习.还望看到文章的同学,前往原创的博客园.感谢msp的昌伟哥哥的分享精神. 移动应用在处理网络资源时 ...

  8. [转载] IOS 获取网络图片的大小 改变 图片色值 灰度什么的方法集合

    IOS 获取网络图片的大小 改变 图片色值 灰度什么的方法集合

  9. iOS网络图片缓存SDWebImage

    Web image(网络图像) 该库提供了一个支持来自Web的远程图像的UIImageView类别 它提供了: 添加网络图像和缓存管理到Cocoa Touch framework的UIImageVie ...

随机推荐

  1. html5 canvas 笔记四(变形 Transformations)

    绘制复杂图形必不可少的方法 save() 保存 canvas 状态 restore() 恢复 canvas 状态 Canvas 的状态就是当前画面应用的所有样式和变形的一个快照. Canvas 的状态 ...

  2. Linux sar分析网卡流量

    yum install sysstat    sar -n { DEV | EDEV | NFS | NFSD | SOCK | ALL }    sar 提供六种不同的语法选项来显示网络信息.-n选 ...

  3. POJ 3903:Stock Exchange(裸LIS + 二分优化)

    http://poj.org/problem?id=3903 Stock Exchange Time Limit: 1000MS   Memory Limit: 65536K Total Submis ...

  4. java double类型保留两位小数4种方法【转】

    4种方法,都是四舍五入,例: import java.math.BigDecimal; import java.text.DecimalFormat; import java.text.NumberF ...

  5. 杭电1020-Encoding

    Problem Description Given a string containing only 'A' - 'Z', we could encode it using the following ...

  6. Robot Motion 分类: POJ 2015-06-29 13:45 11人阅读 评论(0) 收藏

    Robot Motion Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 11262 Accepted: 5482 Descrip ...

  7. Camera 图像处理原理分析

    1         前言 做为拍照手机的核心模块之一,camera sensor效果的调整,涉及到众多的参数,如果对基本的光学原理及sensor软/硬件对图像处理的原理能有深入的理解和把握的话,对我们 ...

  8. vs智能提示突然消失的解决办法 (vs2008 vs2010 vs2012 智能提示)

    vs智能提示突然消失的解决办法 (vs2008 vs2010 vs2012 智能提示) 下面一段话是网上找到的解决方案: 重置Visual Studio可以解决此问题,方法是从开始->Micro ...

  9. Android各种获取Context方法

    首先讲一讲这四个函数的区别,后面还有我对context的一些理解区别如下所示: 原文链接http://stackoverflow.com/questions/6854265/getapplicatio ...

  10. Android Studio教程,Android Studio安装教程

    http://jingyan.baidu.com/article/67662997393cf654d51b8435.html