博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
iOS - ImageCache 网络图片缓存
阅读量:6684 次
发布时间:2019-06-25

本文共 39315 字,大约阅读时间需要 131 分钟。

1、ImageCache

  • 使用内存缓存方式:

    ImageCache1

  • 使用沙盒缓存方式:

    ImageCache2

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

    • 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 
        // 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
      ) 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 
      // 下载图片的 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
      ) 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 
      #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 网址:

  • 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
      ) 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        }

转载地址:http://uzoao.baihongyu.com/

你可能感兴趣的文章
Mysql 备份恢复的简单实践
查看>>
第四、五章解决队列和串的编程问题
查看>>
包失效,无法编译
查看>>
linux 配置全用户的环境变量,profile.d文件的作用
查看>>
程序员成长之路
查看>>
linux邮件服务器配置
查看>>
HTML5学习笔记(二)——表单1
查看>>
我的友情链接
查看>>
docker笔记
查看>>
三层交换机与路由器的相关配置
查看>>
html表单笔记
查看>>
我的友情链接
查看>>
nginx负载均衡的5种策略
查看>>
MyBatis学习总结(三)——优化MyBatis配置文件中的配置
查看>>
jsoup使用post方式查询和解析json
查看>>
翻译软件开发(do it yourself)
查看>>
《Java程序员的基本修养》读书笔记之内存回收
查看>>
鸟哥私房菜重温6
查看>>
适用于ASP等环境的JS日期选择控件
查看>>
rpm 与 yum 用法
查看>>