Objective-c 多线程操作 自定义NSOperation 模拟下载
写在前面
使用多线程下载图片,使用内存缓存和磁盘缓存。
这里只为理解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 模拟下载的更多相关文章
- iOS开发多线程篇—自定义NSOperation
		iOS开发多线程篇—自定义NSOperation 一.实现一个简单的tableView显示效果 实现效果展示: 代码示例(使用以前在主控制器中进行业务处理的方式) 1.新建一个项目,让控制器继承自UI ... 
- iOS开发多线程篇 11 —自定义NSOperation
		iOS开发多线程篇—自定义NSOperation 一.实现一个简单的tableView显示效果 实现效果展示: 代码示例(使用以前在主控制器中进行业务处理的方式) 1.新建一个项目,让控制器继承自UI ... 
- 自定义NSOperation下载图片
		自定义NSOperation的话,只是需要将要下载图片的操作下载它的main方法里面,考虑到,图片下载完毕,需要回传到控制器里,这里可以采用block,也可以采用代理的方式实现,我采用的是代理的方式实 ... 
- iOS多线程编程之自定义NSOperation(转载)
		一.实现一个简单的tableView显示效果 实现效果展示: 代码示例(使用以前在主控制器中进行业务处理的方式) 1.新建一个项目,让控制器继承自UITableViewController. 1 // ... 
- 六:多线程--自定义NSOperation
		一.实现一个简单的tableView显示效果 实现效果展示: 代码示例(使用以前在主控制器中进行业务处理的方式) 1.新建一个项目,让控制器继承自UITableViewController. 3 // ... 
- Android群英传笔记系列三   view的自定义:实现一个模拟下载
		1.实现效果:动态显示进度(分别显示了整个的动态改变的过程,然后完成后,弹出一个对话框) 2.实现过程:可以分为绘制一个圆,圆弧和文本三部分,然后在MainAcitivity中通过线程模拟 ... 
- 多线程编程 (2) -NSOperation
		一.NSInvocationOperation 二.NSBlockOperation 三.NSOperation的其他用法 四.自定义NSOperation 1.上一讲简单介绍了NSThread的使用 ... 
- iOS多线程开发之NSOperation - 快上车,没时间解释了!
		一.什么是NSOperation? NSOperation是苹果提供的一套多线程解决方案.实际上NSOperation是基于GCD更高一层的封装,但是比GCD更加的面向对象.代码可读性更高.可控性更强 ... 
- iOS多线程开发之NSOperation
		一.什么是NSOperation? NSOperation是苹果提供的一套多线程解决方案.实际上NSOperation是基于GCD更高一层的封装,但是比GCD更加的面向对象.代码可读性更高.可控性更强 ... 
随机推荐
- Spring 4学习——问题与注意事项(一)
			1.Spring项目依赖的jar包有5个: 2.applicationContext.xml文件中,如下bean的property的name值对应的是HelloWorld类中的setter方法,即na ... 
- TCP慢启动,拥塞控制,ECN 笔记
			TCP慢启动,拥塞控制,ECN 笔记 1,TCP慢启动 TCP在连接过程的三次握手完成后,开始传数据,并不是一开始向网络通道中发送大量的数据包,这样很容易导致网络中路由器缓存空间耗尽,从而发生拥塞:而 ... 
- (转)对Http协议的长连接和短连接新的认识
			转载来自:http://www.cnblogs.com/zuoxiaolong/p/life49.html一直对长连接短连接模模糊糊,看着该博主的文章,豁然开朗~ 引言 最近刚到公司不到一个月,正处于 ... 
- CentOS 安装数据库笔记
			1.配置YUM源 # 下载mysql源安装包 shell.noarch.rpm # 安装mysql源 shell.noarch.rpm 检查mysql源是否安装成功 shell> yum rep ... 
- android 下 利用webview实现浏览器功能
			android 下 利用webview实现浏览器功能(一): 1.界面添加WEBVIEW控件. 2.在界面.JAVA代码页面(protected void onCreate(Bundle savedI ... 
- ViewPager实现无限轮播踩坑记
			最近笔者想通过ViewPager来实现一个广告Banner,并实现无限轮播的效果,但是在这个过程中踩了不少的坑,听我慢慢道来.如果大家有遇到和我一样的情况,可以参考我的解决方法,没有那就更好,如果针对 ... 
- AngularJS高级程序设计读书笔记 -- 指令篇 之 内置指令
			1. 内置指令(10-12 章) AngularJS 内置超过 50 个内置指令, 包括 数据绑定,表单验证,模板生成,时间处理 和 HTML 操作. 指令暴露了 AngularJS 的核心功能, 如 ... 
- matlab对文件目录进行自然排序
			作者:tongqingliu 转载请注明出处: matlab对文件目录进行自然排序 比如我新建一个tmp文件夹,在该文件夹下新建以下txt文件进行测试 a1.txt a2.txt a3.txt a11 ... 
- Java8 Lambda/Stream使用说明
			一.Stream流1. 流的基本概念 1.1 什么是流?流是Java8引入的全新概念,它用来处理集合中的数据,暂且可以把它理解为一种高级集合.众所周知,集合操作非常麻烦,若要对集合进行筛选.投影,需要 ... 
- google和oracle闹掰,Java 会不会被抛弃?
			眼花缭乱的编程语言 程序界的语言实在太多,但有一种语言不得不说,那就是java语言,Java语言是Android系统的主要开发语言,现在和Google的关系不是很好,但是他会被淘汰吗?下面简单地分析一 ... 
