---恢复内容开始---
lNSOperation和NSOperationQueue实现多线程的具体步骤
先将需要执行的操作封装到一个NSOperation对象中
然后将NSOperation对象添加到NSOperationQueue中
系统会自动将NSOperationQueue中的NSOperation取出来
将取出的NSOperation封装的操作放到一条新线程中执行
注意:NSOperation是一个抽象的类,通过它的三个子类来封装任务 NSIvocationOperation NSBlockOperation 自定义NSOperation的方式
@implementation ViewController -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { NSLog(@"-----"); [self customOperation]; } -(void)invocationOperation { //1.封装操作 /* 第一个参数:目标对象 self 第二个参数:调用方法 第三个参数:调用方法需要传递的参数 */ 通过NSIvocationOPeration封装操作 NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download1) object:nil]; NSInvocationOperation *op2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download1) object:nil]; //2.启动操作 这里必须要启动操作 [op1 start]; [op2 start]; } -(void)blockOperation { //1.封装操作 通过NSBlockOperation封装操作 NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{ //主线程执行 NSLog(@"download1---%@",[NSThread currentThread]); }]; NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"download2---%@",[NSThread currentThread]); }]; NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"download3---%@",[NSThread currentThread]); }]; //追加任务 //追加的任务在子线程中并发执行意味着会开线程 [op3 addExecutionBlock:^{ NSLog(@"download4---%@",[NSThread currentThread]); }]; [op3 addExecutionBlock:^{ NSLog(@"download5---%@",[NSThread currentThread]); }]; [op3 addExecutionBlock:^{ NSLog(@"download6---%@",[NSThread currentThread]); }]; //2.开始执行 [op1 start]; [op2 start]; [op3 start]; } -(void)customOperation { //1.封装任务 自定义NSOperation的方式封装任务也就是创建一个继承与NSOperation的类 重写-main方法,在方法中写要执行的任务 /*内部会调用MSHOperation的main方法*/ MSHOperation *op1 = [[MSHOperation alloc]init]; MSHOperation *op2 = [[MSHOperation alloc]init]; //2.开始执行 [op1 start]; [op2 start]; } -(void)download1 { NSLog(@"download1---%@",[NSThread currentThread]); }
以上直接封装操作,没什么作用,只有将封装的操作NAOperation对象放进NSOperationQueue才会创建线程
注意:默认异步,并发执行的执行的,如果想在控制它像GCD中的串行执行人任务的话,通过队列的最大并发数来设置(
queue.maxConcurrentOperationCount = 1;
)
接下是将封装的操作放进NSOperationqueu中
@implementation ViewController -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { NSLog(@"-----"); [self customOperationWithQueue]; } //GCD /* 01 串行队列 自己创建|主队列 02 并发队列 自己创建|全局并发队列 */ //NSOperation /* 获取队列的两种方式 01 主队列 同GCD mainQueue 刷新UI 02 非主队列 alloc init 特点:同时具备了串行和并发的功能|默认是并发的 */ -(void)invocationOperationWithQueue { //0.创建队列 NSOperationQueue *queue = [[NSOperationQueue alloc]init]; //1.封装操作 /* 第一个参数:目标对象 self 第二个参数:调用方法 第三个参数:调用方法需要传递的参数 */ NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download1) object:nil]; NSInvocationOperation *op2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download2) object:nil]; //2.启动操作 //[op1 start]; 将NSOperation封装到队列中,不需要调用该方法,内部默认会执行该方法 //[op2 start]; //2.把操作添加到队列 在队列中添加多个NSOperation [queue addOperation:op1]; //[op1 start] [queue addOperation:op2]; //[op2 start] } -(void)blockOperationWithQueue { //0.创建队列 NSOperationQueue *queue = [[NSOperationQueue alloc]init]; //1.封装操作 NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{ //主线程执行 NSLog(@"download1---%@",[NSThread currentThread]); }]; NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"download2---%@",[NSThread currentThread]); }]; NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"download3---%@",[NSThread currentThread]); }]; //追加任务 //追加的任务在子线程中并发执行 [op3 addExecutionBlock:^{ NSLog(@"download4---%@",[NSThread currentThread]); }]; [op3 addExecutionBlock:^{ NSLog(@"download5---%@",[NSThread currentThread]); }]; [op3 addExecutionBlock:^{ NSLog(@"download6---%@",[NSThread currentThread]); }]; //2.开始执行 // [op1 start]; 将封装操作的NSBlockOperation对象添加到队列中,内部也会调用- start方法 // [op2 start]; // [op3 start]; [queue addOperation:op1]; [queue addOperation:op2]; [queue addOperation:op3];
开方法常用简便的方法,下面是它的原理 //简便方法:该方法内部会自动将block块里面的任务封装为一个NSBlockOperation对象,然后添加到队列 [queue addOperationWithBlock:^{ NSLog(@"download7---%@",[NSThread currentThread]); }]; } -(void)customOperationWithQueue { //0.创建队列 NSOperationQueue *queue = [[NSOperationQueue alloc]init]; //1.封装任务 /*内部会调用MSHOperation的main方法*/ MSHOperation *op1 = [[MSHOperation alloc]init]; MSHOperation *op2 = [[MSHOperation alloc]init]; //2.开始执行 //[op1 start]; //main 自定义的NSOperation内部会调用Start方法,并且也会调用它的-main方法 //[op2 start]; [queue addOperation:op1]; [queue addOperation:op2]; }
下面是NSOperationqueu的一些常用属性的使用
最大并发数 同时执行的任务数 比如,同时开3个线程执行3个任务,并发数就是3 最大并发数的相关方法 - (NSInteger)maxConcurrentOperationCount; - (void)setMaxConcurrentOperationCount:(NSInteger)cnt; 队列的取消、暂停、恢复 - (void)cancelAllOperations; 提示:也可以调用NSOperation的- (void)cancel方法取消单个操作 暂停和恢复队列 - (void)setSuspended:(BOOL)b; // YES代表暂停队列,NO代表恢复队列 - (BOOL)isSuspended; 操作依赖 NSOperation之间可以设置依赖来保证执行顺序 比如一定要让操作A执行完后,才能执行操作B,可以这么写 [operationB addDependency:operationA]; // 操作B依赖于操作A 可以在不同queue的NSOperation之间创建依赖关系
注意:不能相互依赖
p比如A依赖B,B依赖A
监听操作完成
-(void)maxCount { //0.创建队列 NSOperationQueue *queue = [[NSOperationQueue alloc]init]; //设置最大并发数 //-1 表示不受限制 queue.maxConcurrentOperationCount = 0; //1.封装操作 NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{ //主线程执行 NSLog(@"download1---%@",[NSThread currentThread]); }]; NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"download2---%@",[NSThread currentThread]); }]; NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"download3---%@",[NSThread currentThread]); 这里监听的是op3对象的任务完成
[op3 setCompletionBlock:^{ NSLog(@"download3下载完成"); }]; }]; [queue addOperation:op1]; [queue addOperation:op2]; [queue addOperation:op3]; }
NSOperation线程之间的通信
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { //0.创建队列 NSOperationQueue *queue = [[NSOperationQueue alloc]init]; //1.封装操作 NSBlockOperation *download = [NSBlockOperation blockOperationWithBlock:^{ NSURL *url = [NSURL URLWithString:@"http://static.fever38.com/hotpolls/option_pic/2013032019073965331_500X.png"]; NSData *data = [NSData dataWithContentsOfURL:url]; UIImage *image = [UIImage imageWithData:data]; NSLog(@"download--%@",[NSThread currentThread]); //回到主线程刷洗UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{ self.imageView.image = image; NSLog(@"UI--%@",[NSThread currentThread]); }]; }]; //2.添加操作到队列 [queue addOperation:download]; }
多图下载的案例
@interface ViewController ()
/** tableView的数据源*/
@property (nonatomic ,strong)NSArray *apps;
/** 内存缓存*/
@property (nonatomic ,strong)NSMutableDictionary *images;
@property (nonatomic ,strong) NSOperationQueue *queue;
/** 操作缓存*/
@property (nonatomic ,strong)NSMutableDictionary *operations;
@end
@implementation ViewController
#pragma mark ----------------------
#pragma mark Lazy loading
-(NSMutableDictionary *)operations
{
if (_operations == nil) {
_operations = [NSMutableDictionary dictionary];
}
return _operations;
}
-(NSOperationQueue *)queue
{
if (_queue == nil) {
_queue = [[NSOperationQueue alloc]init];
_queue.maxConcurrentOperationCount = 6;
}
return _queue;
}
-(NSMutableDictionary *)images
{
if (_images == nil) {
_images = [NSMutableDictionary dictionary];
}
return _images;
}
-(NSArray *)apps
{
if (_apps == nil) {
//加载数据
NSArray *arrayM = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle]pathForResource:@"apps.plist" ofType:nil]];
//字典转模型 字典数组--->模型数组
NSMutableArray *arrM = [NSMutableArray arrayWithCapacity:arrayM.count];
for (NSDictionary *dict in arrayM) {
[arrM addObject:[MSHApp appWithDict:dict]];
}
_apps = arrM;
NSLog(@"%@",arrM);
}
return _apps;
}
#pragma mark ----------------------
#pragma mark UITablViewDataSource
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.apps.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//1.确定标识符
NSString *ID = @"app";
//2.创建cell
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
//3.设置cell
//拿到该行cell对应的数据模型
MSHApp *appM = self.apps[indexPath.row];
//设置标题和子标题
cell.textLabel.text = appM.name;
cell.detailTextLabel.text = appM.download;
// 加载图片先根据key去缓存中去取
UIImage *image = [self.images objectForKey:appM.icon];
if (image) {
cell.imageView.image = image;
}else
{
如果缓存区中没有,则去沙盒中去取
//现尝试去沙河中取,如果没有那么才下载
NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSString *fileName = [appM.icon lastPathComponent];
//文件的全路径
NSString *fullPth = [caches stringByAppendingPathComponent:fileName];
//NSLog(@"%@",fullPth);
NSData *data = [NSData dataWithContentsOfFile:fullPth];
//沙盒缓存清空
data = nil;
if (data) {
UIImage *image = [UIImage imageWithData:data];
cell.imageView.image = image;
//保存图片到内存缓存 注意:这里要记得从沙盒中获取图片以后,要添加到缓存区中
[self.images setObject:image forKey:appM.icon];
NSLog(@"沙河中有这样图片了");
}else
{
如果沙盒中也没有,先设置站位图片,在根据URL区下载照片
//设置展占位图片
cell.imageView.image = [UIImage imageNamed:@"Snip20151222_498"];
//查看该图片的下载操作是否存在 线程是并发执行的,先判断有没有下载操作存在
NSBlockOperation *download = [self.operations objectForKey:appM.icon];
if (download == nil) {
download = [NSBlockOperation blockOperationWithBlock:^{
//显示图片
NSURL *url = [NSURL URLWithString:appM.icon];
[NSThread sleepForTimeInterval:1.0];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
NSLog(@"-下载图片操作---%zd",indexPath.row);
if (image == nil) {
[self.operations removeObjectForKey:appM.icon];
return ;
}
//保存图片到内存缓存
[self.images setObject:image forKey:appM.icon];
//保存图片到沙河缓存
[data writeToFile:fullPth atomically:YES];
//线程间通信
[[NSOperationQueue mainQueue]addOperationWithBlock:^{
// NSLog(@"----%zd",indexPath.row);
// cell.imageView.image = image;
//刷新某一行
[tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}];
[self.operations removeObjectForKey:appM.icon];
}];
//加入到操作缓存
[self.operations setObject:download forKey:appM.icon];
//把操作添加到队列
[self.queue addOperation:download];
}
}
}
//4.返回cell
return cell;
}
/*
Documents:会备份,不能把下载的数据保存在这个文件下面
Libriary
caches
preferences:偏好设置
tmp:临时文件夹
*/
//问题
//01 UI不流畅 ------>开子线程下载
//01 数据不显示
//02 图片重复下载 ---操作缓存
//02 重复下载 ----内存缓存|优化(沙河缓存)
多图下载的案例(通过SDWebImageView)
框架下载地址 https://github.com/rs/SDWebImage
#import "ViewController.h" #import "MSHApp.h" 自定义模型 #import "UIImageView+WebCache.h" 包含的框架的头文件 @interface ViewController () /** tableView的数据源*/ @property (nonatomic ,strong)NSArray *apps; /** 内存缓存*/ @property (nonatomic ,strong)NSMutableDictionary *images; 缓存 @property (nonatomic ,strong) NSOperationQueue *queue; 存储操作 /** 操作缓存*/ @property (nonatomic ,strong)NSMutableDictionary *operations; @end @implementation ViewController #pragma mark ---------------------- #pragma mark Lazy loading -(NSMutableDictionary *)operations { if (_operations == nil) { _operations = [NSMutableDictionary dictionary]; } return _operations; } -(NSOperationQueue *)queue { if (_queue == nil) { _queue = [[NSOperationQueue alloc]init]; _queue.maxConcurrentOperationCount = 6; } return _queue; } -(NSMutableDictionary *)images { if (_images == nil) { _images = [NSMutableDictionary dictionary]; } return _images; } -(NSArray *)apps { if (_apps == nil) { //加载数据 NSArray *arrayM = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle]pathForResource:@"apps.plist" ofType:nil]]; //字典转模型 字典数组--->模型数组 NSMutableArray *arrM = [NSMutableArray arrayWithCapacity:arrayM.count]; for (NSDictionary *dict in arrayM) { [arrM addObject:[MSHApp appWithDict:dict]]; } _apps = arrM; NSLog(@"%@",arrM); } return _apps; } #pragma mark ---------------------- #pragma mark UITablViewDataSource -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.apps.count; } -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { //1.确定标识符 NSString *ID = @"app"; //2.创建cell UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID]; //3.设置cell //拿到该行cell对应的数据模型 MSHApp *appM = self.apps[indexPath.row]; //设置标题和子标题 cell.textLabel.text = appM.name; cell.detailTextLabel.text = appM.download;
这里这需要一句话,解决上面的所有逻辑 牛逼!!!!吧
第一个参数是图片的URL
第二个图片是站位图片
[cell.imageView sd_setImageWithURL:[NSURL URLWithString:appM.icon] placeholderImage:[UIImage imageNamed:@"Snip20151222_498"]]; NSLog(@"%@",[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]); //4.返回cell return cell; }
---恢复内容结束---