写在前面

使用多线程下载图片,使用内存缓存和磁盘缓存。

这里只为理解NSOperation及其派生类

真要应用到APP中 请下载成熟的第三方库

效果

下载多张图片时可控制线程并发数

分析

  • 自定义NSOperation 执行下载操作
  • 封装一个队列操作,创建N个线程,队列控制并发
  • 通过线程tag查找对应图片
  • 通过队列tag查找对应队列
  • 缓存设置 内存缓存和磁盘缓存
  • 下载完成通过delegate或block进行回调通知

代码

  • 通用内容 线程完成时的回调 、加载图片时的回调
#ifndef Uinty_h
#define Uinty_h
#import <UIKit/UIKit.h> typedef void(^DownloadImageDataBlock)(NSData *data,int tag);
typedef void(^DownloadImageBlock)(UIImage *image,int tag,int queueTag); static int const kImageViewTag = 1990;
//线程操作协议
@protocol DownloadOperationDelegate <NSObject>
//线程下载数据完成
- (void)downloadOperationWithData:(NSData*)data withTag:(int)tag; @end //下载操作协议
@protocol DownloadImageDelegate <NSObject>
//图片回调
- (void)downloadImageFinishedWith:(UIImage*)image andTag:(int)tag withQueueTag:(int)queueTag;
@end
#endif /* Uinty_h */
  • 自定义NSOperation 下载数据
#import <Foundation/Foundation.h>
#import "Uinty.h"
@interface DownloadOperation : NSOperation //block
@property (nonatomic,copy)DownloadImageDataBlock imageDataBlock;
//标识
@property (nonatomic,assign)int tag; //代理
@property (nonatomic,strong)id<DownloadOperationDelegate> delegate; //初始化
- (instancetype)initWithUrlStr:(NSString*)urlStr; + (instancetype)downloadOperationWithUrlStr:(NSString*)urlStr;
@end #import "DownloadOperation.h"
@interface DownloadOperation()
@property(nonatomic,copy)NSString *urlStr; @end
@implementation DownloadOperation

- (instancetype)initWithUrlStr:(NSString*)urlStr {
self = [super init];
if (self) {
self.urlStr = urlStr;
}
return self;
} + (instancetype)downloadOperationWithUrlStr:(NSString*)urlStr {
return [[DownloadOperation alloc] initWithUrlStr:urlStr];
} -(void)main { NSURL *url = [NSURL URLWithString:self.urlStr];
NSData *data = [NSData dataWithContentsOfURL:url];
//模拟耗时
sleep(1);
//返回主线程
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
//block通知
if (self.imageDataBlock) {
self.imageDataBlock(data,self.tag);
}
//代理通知
if ([self.delegate respondsToSelector:@selector(downloadOperationWithData:withTag:)]) {
[self.delegate downloadOperationWithData:data withTag:self.tag];
}
}];
}
@end
  • 加载图片:缓存加载和线程队列加载
#import <Foundation/Foundation.h>
#import "DownloadOperation.h"
@interface DownloadImage : NSObject
//加载完成
@property (nonatomic,copy)DownloadImageBlock downloadFinishedBlock;
//加载单张时使用
@property (nonatomic,copy)NSString *urlStr;
//加载多张时使用
@property (nonatomic,strong)NSArray *urlArray;
//队列线程最大并发数
@property (nonatomic,assign)int maxOperationCount;
//代理
@property (nonatomic,strong)id<DownloadImageDelegate> delegate;
//标识
@property (nonatomic,assign)int tag;
//磁盘缓存 内存缓存 单位:M
@property (nonatomic,assign)NSUInteger diskCapacity;
@property (nonatomic,assign)NSUInteger MemoryCapacity; //初始化传url数组
- (instancetype)initWithUrlStrArray:(NSArray<NSString*>*)urlArray withStartTag:(int)startTag ;
//初始化单url
- (instancetype)initWithUrlStr:(NSString*)urlStr ;
//类工厂
+ (instancetype)downloadImageWithUrlStrArray:(NSArray<NSString*>*)urlArray withStartTag:(int)startTag ;
+ (instancetype)downloadImageWithUrlStr:(NSString*)urlStr ; //开始下载
- (void)starDownloadImage;
@end
//
// DownloadImage.m
// DownloadImageDemo
//
// Created by gongwenkai on 2017/1/3.
// Copyright © 2017年 gongwenkai. All rights reserved.
// #import "DownloadImage.h"
#import "DownloadOperation.h" @interface DownloadImage()
@property (nonatomic,strong)NSOperationQueue *queue;
@property (nonatomic,strong)NSCache *imageCache;
@property (nonatomic,copy)NSString *cachePath;
@property (nonatomic,assign)int imageStartTag; @end @implementation DownloadImage #pragma mark - 懒加载
- (NSString *)cachePath {
if (!_cachePath) {
_cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
}
return _cachePath;
} - (NSCache *)imageCache {
if (!_imageCache) {
_imageCache = [[NSCache alloc] init];
_imageCache.countLimit = 100; }
return _imageCache;
} - (NSOperationQueue *)queue {
if (!_queue) {
_queue = [[NSOperationQueue alloc] init];
}
return _queue;
} - (NSUInteger)MemoryCapacity {
if (!_MemoryCapacity) {
_MemoryCapacity = 1;
}
return _MemoryCapacity;
} - (NSUInteger)diskCapacity {
if (!_diskCapacity) {
_diskCapacity = 10;
}
return _diskCapacity;
} - (int)maxOperationCount {
if (!_maxOperationCount) {
_maxOperationCount = 2;
}
return _maxOperationCount;
} #pragma mark - 初始化
+ (instancetype)downloadImageWithUrlStrArray:(NSArray<NSString*>*)urlArray withStartTag:(int)startTag {
return [[DownloadImage alloc] initWithUrlStrArray:urlArray withStartTag:startTag];
} + (instancetype)downloadImageWithUrlStr:(NSString*)urlStr {
return [[DownloadImage alloc] initWithUrlStr:urlStr];
} - (instancetype)initWithUrlStrArray:(NSArray<NSString*>*)urlArray withStartTag:(int)startTag{
self = [super init];
if (self) {
self.urlArray = urlArray;
self.imageStartTag = startTag;
}
return self;
} - (instancetype)initWithUrlStr:(NSString*)urlStr {
self = [super init];
if (self) {
self.urlStr = urlStr;
}
return self;
} //初始化 set方法 单个url装进数组
- (void)setUrlStr:(NSString *)urlStr {
_urlStr = urlStr;
_urlArray = [NSArray arrayWithObject:urlStr];
} #pragma mark - 加载图片
- (void)starDownloadImage { //设置内存缓存和磁盘缓存大小
NSURLCache *URLCache = [[NSURLCache alloc] initWithMemoryCapacity:self.MemoryCapacity * 1024 * 1024 diskCapacity:self.diskCapacity * 1024 * 1024 diskPath:nil];
[NSURLCache setSharedURLCache:URLCache]; //加载数据
for (int i = 0; i < self.urlArray.count; i++) {
int imageTag = self.imageStartTag + i;
NSString *urlStr = self.urlArray[i];
//从内存缓存中读取图片
UIImage *memoryImage = [self.imageCache objectForKey:urlStr];
if (memoryImage) {
//block回调结果
if (self.downloadFinishedBlock) {
self.downloadFinishedBlock(memoryImage,imageTag,self.tag);
}
//代理回调结果
if ([self.delegate respondsToSelector:@selector(downloadImageFinishedWith:andTag:withQueueTag:)]) {
[self.delegate downloadImageFinishedWith:memoryImage andTag:imageTag withQueueTag:self.tag];
}
continue;
}
//从磁盘缓存中读取图片
NSString *imagePath=[urlStr lastPathComponent];
NSString *imageCachePath = [self.cachePath stringByAppendingPathComponent:imagePath];
NSData *data = [NSData dataWithContentsOfFile:imageCachePath];
if (data) {
UIImage *diskImage = [UIImage imageWithData:data];
//block回调结果
if (self.downloadFinishedBlock) {
self.downloadFinishedBlock(diskImage,imageTag,self.tag);
}
//代理回调结果
if ([self.delegate respondsToSelector:@selector(downloadImageFinishedWith:andTag:withQueueTag:)]) {
[self.delegate downloadImageFinishedWith:diskImage andTag:imageTag withQueueTag:self.tag];
}
continue;
} //创建线程加载图片
self.queue.maxConcurrentOperationCount = self.maxOperationCount;
DownloadOperation *op = [DownloadOperation downloadOperationWithUrlStr:urlStr]; op.tag = i + kImageViewTag;
if (self.urlArray.count < self.maxOperationCount) {
[op start];
}else {
[self.queue addOperation:op];
} //线程回调结果
op.imageDataBlock = ^(NSData *data,int tag){
UIImage *image = [UIImage imageWithData:data]; //写入内存缓存
[self.imageCache setObject:image forKey:urlStr];
//写入磁盘缓存
[data writeToFile:imageCachePath atomically:YES]; //block回调
if (self.downloadFinishedBlock) {
NSLog(@"tage ======%d",tag);
self.downloadFinishedBlock(image,tag,self.tag);
}
//代理回调
if ([self.delegate respondsToSelector:@selector(downloadImageFinishedWith:andTag:withQueueTag:)]) {
[self.delegate downloadImageFinishedWith:image andTag:tag withQueueTag:self.tag];
} }; } } @end
  • 外部调用及测试
- (void)viewDidLoad {
[super viewDidLoad]; for (int i = 0; i < 6; i++) {
UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake(0, i*105, 100, 100)];
imgView.tag = i + kImageViewTag;
imgView.backgroundColor = [UIColor redColor];
[self.view addSubview:imgView];
} UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake(120, 100, 100, 100)];
imgView.backgroundColor = [UIColor redColor];
imgView.tag = 1234;
[self.view addSubview:imgView];
NSLog(@"%@",NSHomeDirectory());
}
- (IBAction)clickLoadImages:(id)sender {
//下载多张图片
DownloadImage *download = [DownloadImage downloadImageWithUrlStrArray:@[@"http://img.hb.aicdn.com/6e004cb3c5f58f016a57b90f8bbb93d7075453f2efd0-anTckt_fw658",@"http://img.hb.aicdn.com/fb18b522caf2821adb7af96f8656787f8d9bdad31bdec-6f6h4X_fw658",@"http://img.hb.aicdn.com/22e4dfd8d135de7ae0d451e351c00bddf732919920840-BMRw4Z_fw658",@"http://img.hb.aicdn.com/536c96af48b38faca5bcad20ba0ea6aba8929b711e5b4-lvdBlI_fw658",@"http://img.hb.aicdn.com/055e5458bd340a52ca0067f5d7c22b6c3b18d119292ae-QLEsYp_fw658",@"http://img.hb.aicdn.com/b50481ab8a2b4e3587068df0552ebad08409f0b3ca23-8gBQ9x_fw658"] withStartTag:kImageViewTag];
//设置并发数
download.maxOperationCount = 3;
download.tag = 0;
download.delegate = self;
/* block 回调 结果
download.downloadFinishedBlock = ^(UIImage *image,int tag,int queueTag) {
UIImageView *img = [self.view viewWithTag:tag];
img.image = image;
NSLog(@"jicia====%d",tag);
};
*/
//开始下载
[download starDownloadImage];
} - (IBAction)clickLoadSingalImage:(id)sender {
//单张下载
DownloadImage *down = [DownloadImage downloadImageWithUrlStr:@"http://img.hb.aicdn.com/b50481ab8a2b4e3587068df0552ebad08409f0b3ca23-8gBQ9x_fw658"];
down.tag = 1;
down.delegate = self; /* block 回调 结果
down.downloadFinishedBlock = ^(UIImage *image,int tag,int queueTag) {
UIImageView *imgV = [self.view viewWithTag:1234];
imgV.image = image;
};
*/
[down starDownloadImage]; }
- (IBAction)cleanAll:(id)sender { for (int i = 0; i < 6; i++) {
UIImageView *imgView = [self.view viewWithTag:i + kImageViewTag];
imgView.image = nil;
}
UIImageView *imgView = [self.view viewWithTag:1234];
imgView.image = nil; } //通过代理回调操作
- (void)downloadImageFinishedWith:(UIImage*)image andTag:(int)tag withQueueTag:(int)queueTag{
if (queueTag == 0) {
UIImageView *img = [self.view viewWithTag:tag];
img.image = image;
} else if (queueTag == 1) {
UIImageView *imgV = [self.view viewWithTag:1234];
imgV.image = image;
} }

DEMO地址

https://github.com/gongxiaokai/DownloadImageDemo

Objective-c 多线程操作 自定义NSOperation 模拟下载的更多相关文章

  1. iOS开发多线程篇—自定义NSOperation

    iOS开发多线程篇—自定义NSOperation 一.实现一个简单的tableView显示效果 实现效果展示: 代码示例(使用以前在主控制器中进行业务处理的方式) 1.新建一个项目,让控制器继承自UI ...

  2. iOS开发多线程篇 11 —自定义NSOperation

    iOS开发多线程篇—自定义NSOperation 一.实现一个简单的tableView显示效果 实现效果展示: 代码示例(使用以前在主控制器中进行业务处理的方式) 1.新建一个项目,让控制器继承自UI ...

  3. 自定义NSOperation下载图片

    自定义NSOperation的话,只是需要将要下载图片的操作下载它的main方法里面,考虑到,图片下载完毕,需要回传到控制器里,这里可以采用block,也可以采用代理的方式实现,我采用的是代理的方式实 ...

  4. iOS多线程编程之自定义NSOperation(转载)

    一.实现一个简单的tableView显示效果 实现效果展示: 代码示例(使用以前在主控制器中进行业务处理的方式) 1.新建一个项目,让控制器继承自UITableViewController. 1 // ...

  5. 六:多线程--自定义NSOperation

    一.实现一个简单的tableView显示效果 实现效果展示: 代码示例(使用以前在主控制器中进行业务处理的方式) 1.新建一个项目,让控制器继承自UITableViewController. 3 // ...

  6. Android群英传笔记系列三 view的自定义:实现一个模拟下载

    1.实现效果:动态显示进度(分别显示了整个的动态改变的过程,然后完成后,弹出一个对话框)       2.实现过程:可以分为绘制一个圆,圆弧和文本三部分,然后在MainAcitivity中通过线程模拟 ...

  7. 多线程编程 (2) -NSOperation

    一.NSInvocationOperation 二.NSBlockOperation 三.NSOperation的其他用法 四.自定义NSOperation 1.上一讲简单介绍了NSThread的使用 ...

  8. iOS多线程开发之NSOperation - 快上车,没时间解释了!

    一.什么是NSOperation? NSOperation是苹果提供的一套多线程解决方案.实际上NSOperation是基于GCD更高一层的封装,但是比GCD更加的面向对象.代码可读性更高.可控性更强 ...

  9. iOS多线程开发之NSOperation

    一.什么是NSOperation? NSOperation是苹果提供的一套多线程解决方案.实际上NSOperation是基于GCD更高一层的封装,但是比GCD更加的面向对象.代码可读性更高.可控性更强 ...

随机推荐

  1. 利用document的readyState去实现页面加载中的效果

    打开新的网页时,为了增强友好性体验,告知用户网页正在加载数据需要呈现一个"页面加载中"之类的提示,只需要利用document就可以实现. 实现示例代码如下: <style&g ...

  2. python每天一个小练习-强壮的密码

    强壮的密码 题目来源 checkio 需求 斯蒂芬和索菲亚对于一切都使用简单的密码,忘记了安全性.请你帮助尼古拉开发一个密码安全检查模块 如果密码的长度大于或等于10个符号,至少有一个数字,一个大写字 ...

  3. 几个常用的linux命令(操作服务器时会用到)

    目录 tmux 背景 安装 使用 启动一个tmux session 暂时离开当前session 回到之前的session 重命名session 创建window 创建pane ps scp 参考 tm ...

  4. js中的事件,内置对象,正则表达式

    [JS中的事件分类] 1.鼠标事件: click/dbclick/mouseover/mouseout/mousemove/mousedown/mouseup 2.键盘事件: keydown: 键盘按 ...

  5. 2017最新技术java高级架构、千万高并发、分布式集群、架构师入门到精通视频教程

    * { font-family: "Microsoft YaHei" !important } h1 { color: #FF0 } 15套java架构师.集群.高可用.高可扩展. ...

  6. JS函数和对象(一)

    在本文章中,将对JS中的函数和对象进行一些讲解,不对之处还请之处 一.JS中的函数 1.1无参函数 其形式如下代码所示 function box(){ alert("我是一个函数,只有被调用 ...

  7. AngularJS4.0环境搭建详解--windows系统环境

    第一步:安装NodeJS 下载最新版的NodeJS并安装,安装完成后打开CMD命令行,输入以下命令: node -v 若返回类似版本号则代表NodeJS安装成功,如下: 第二部 安装npm 新版的No ...

  8. c#面向对象-类(类及其构成)

    学习c#已经快一个学期,在这一段时间里,通过自己的努力和老师指导,自己感觉收获颇丰,所以我想把自己学到东西整理一下,供大家点评!若有错误或不当之处,敬请指出. 今天,我先从类及其构成说起! 1.    ...

  9. APP测试相关点归纳

    APP测试相关点归纳   1.1测试周期 测试周期可按项目的开发周期来确定测试时间,一般测试时间为一两周,根据项目情况以及版本质量可适当缩短或延长测试时间.正式测试前需确认项目排期. 1.2测试资源 ...

  10. sublime text 3 配置python IDE

    Python越来越受“程序猿”们的青睐.快速的开发模式,简洁的代码格式,海量的扩展,这无疑都为python的火热奠定了基础. “磨刀不误砍柴工”,一款功能强劲的IDE能帮助开发者有效的管理.编辑,运行 ...