-------NSOperation简介----
1.NSOperation的作用
·配合使用NSOperation和NSOperationQueue也能实现多线程编程
2.NSOperation和NSOperationQueue实现多线程的具体步骤
·先将需要执行的操作封装到一个NSOperation对象中
·然后将NSOperation对象添加到NSOperationQueue中
·系统会自动将NSOperation中封装的操作放到一条新线程中执行
---------NSOperation的子类----
3.NSOperation是个抽象类,并不具备封装操作的能力,必须使用它的子类
4.使用NSOperation子类的方式有3种
·NSInvocationOperation
·NSBlockOperation
·自定义子类继承NSOperation,实现内部相应的方法
------NSInvocationOperation---
5.创建NSInvocationOperation对象
- (id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg;
6.调用start方法开始执行操作
- (void)start;
一旦执行操作,就会调用target的sel方法
7.注意
·默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作
·只有将NSOperation放到一个NSOperationQueue中,才会异步执行操作
-------NSBlockOperation--
8.创建NSBlockOperation对象
+ (id)blockOperationWithBlock:(void (^)(void))block;
9.通过addExecutionBlock:方法添加更多的操作
- (void)addExecutionBlock:(void (^)(void))block;
注意:只要NSBlockOperation封装的操作数 > 1,就会异步执行操作
-------NSOperationQueue----
10.NSOperationQueue的作用
·NSOperation可以调用start方法来执行任务,但默认是同步执行的
·如果将NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperation中的操作
11.添加操作到NSOperationQueue中
- (void)addOperation:(NSOperation *)op;
- (void)addOperationWithBlock:(void (^)(void))block;
-------最大并发数----
12.什么是并发数
·同时执行的任务数
·比如,同时开3个线程执行3个任务,并发数就是3
13.最大并发数的相关方法
- (NSInteger)maxConcurrentOperationCount;
- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
-----队列的取消、暂停、恢复------
14.取消队列的所有操作
- (void)cancelAllOperations;
提示:也可以调用NSOperation的- (void)cancel方法取消单个操作
15.暂停和恢复队列
- (void)setSuspended:(BOOL)b; // YES代表暂停队列,NO代表恢复队列
- (BOOL)isSuspended;
----操作优先级-----
16.设置NSOperation在queue中的优先级,可以改变操作的执行顺序
- (NSOperationQueuePriority)queuePriority;
- (void)setQueuePriority:(NSOperationQueuePriority)p;
17.优先级的取值(优先级越高,越先执行)
·NSOperationQueuePriorityVeryLow = -8L,
·NSOperationQueuePriorityLow = -4L,
·NSOperationQueuePriorityNormal = 0,
·NSOperationQueuePriorityHigh = 4,
·NSOperationQueuePriorityVeryHigh = 8
---操作依赖---
18.NSOperation之间可以设置依赖来保证执行顺序
·比如一定要让操作A执行完后,才能执行操作B,可以这么写
[operationB addDependency:operationA]; // 操作B依赖于操作A
19.可以在不同queue的NSOperation之间创建依赖关系
20. 注意:不能相互依赖
·比如A依赖B,B依赖A
----操作的执行顺序---
21.对于添加到queue中的operations,它们的执行顺序取决于2点
·首先依据NSOperation之间的依赖关系
·然后依据NSOperation的优先级
22.因此,总体的执行顺序是
·先满足依赖关系
·然后再从NSOperation中选择优先级最高的那个执行
---自定义NSOperation---
23.自定义NSOperation的步骤很简单
·重写- (void)main方法,在里面实现想执行的任务
24.重写- (void)main方法的注意点
·自己创建自动释放池(因为如果是异步操作,无法访问主线程的自动释放池)
·经常通过- (BOOL)isCancelled方法检测操作是否被取消,对取消做出响应
--------分割线---
NSOperation小代码1,
只写一个文件参考下
// DYFViewController.m
// 624-03-NSOperation
//
// Created by dyf on 14-6-24.
// Copyright (c) 2014年 ___FULLUSERNAME___. All rights reserved.
//
#import "DYFViewController.h"
@interface DYFViewController ()
@end
@implementation DYFViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self testOperationQueue];
}
- (void)testOperationListen {
NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"下载图片1111111%@", [NSThread currentThread]);
// 下载图片
}];
operation3.completionBlock = ^{
// 下载完图片后想做的时期
};
// 2.创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:operation3];
}
- (void)testOperationQueue {
// 1.封装操作
NSInvocationOperation *operation1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download) object:nil];
NSInvocationOperation *operation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1111111%@", [NSThread currentThread]);
}];
[operation3 addExecutionBlock:^{
NSLog(@"222222%@", [NSThread currentThread]);
}];
[operation3 addExecutionBlock:^{
NSLog(@"33333%@", [NSThread currentThread]);
}];
// 2.创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 5以内,2~3为宜
queue.maxConcurrentOperationCount = 2;
#warning 面试题
// 设置操作依赖(一定要在添加到队列中前设置)
[operation2 addDependency:operation1]; // 执行顺序取决于依赖,先执行完operation1再执行operation2
// 注意:不能相互依赖,循环操作
// 3.添加操作到队列中(自动执行操作,自动开启线程)
[queue addOperation:operation1];
[queue addOperation:operation2];
[queue addOperation:operation3];
// 取消所有线程
//[queue cancelAllOperations];
// 暂停队列
//[queue setSuspended:YES];
// 设置操作优先级
//operation1.queuePriority = NSOperationQueuePriorityVeryHigh;
}
- (void)testNSBlockOperation {
// 1.创建操作对象,封装要执行的任务
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 11; i++) {
NSLog(@"1111111%@", [NSThread currentThread]);
}
}];
// 任务数在2各以上,就会开线程
[operation addExecutionBlock:^{
for (int i = 0; i < 11; i++) {
NSLog(@"222222%@", [NSThread currentThread]);
}
}];
[operation addExecutionBlock:^{
for (int i = 0; i < 11; i++) {
NSLog(@"33333%@", [NSThread currentThread]);
}
}];
// 2.执行操作
[operation start];
}
- (void)testNSInvocationOperation {
// 1.创建操作对象,封装要执行的任务
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download) object:nil];
// 2.执行操作(默认情况下,若操作没有放到队列queue中,都是同步执行)
[operation start];
}
- (void)download
{
for (int i = 0; i < 11; i++) {
NSLog(@"download-----%@", [NSThread currentThread]);
}
}
- (void)run
{
for (int i = 0; i < 11; i++) {
NSLog(@"run------%@", [NSThread currentThread]);
}
}
@end
----自定义NSOperation---
//
// DYFDownloadOperation.h
// 624-05-自定义Operation
//
// Created by dyf on 14-6-24.
// Copyright (c) 2014年 dyf. All rights reserved.
//
#import <Foundation/Foundation.h>
@class DYFDownloadOperation;
@protocol DYFDownloadOperationDelegate <NSObject>
@optional
- (void)downloadOperation:(DYFDownloadOperation *)operation didFinishedDownload:(UIImage *)image;
@end
@interface DYFDownloadOperation : NSOperation
@property (nonatomic, copy) NSString *url;
@property (nonatomic, strong) NSIndexPath *indexPath;
@property (nonatomic, weak) id<DYFDownloadOperationDelegate> delegate;
@end
//
// DYFDownloadOperation.m
// 624-05-自定义Operation
//
// Created by dyf on 14-6-24.
// Copyright (c) 2014年 dyf. All rights reserved.
//
#import "DYFDownloadOperation.h"
@implementation DYFDownloadOperation
/**
* 在main方法中实现具体操作
*/
- (void)main
{
@autoreleasepool {
if (self.isCancelled) return;
NSURL *imaUrl = [NSURL URLWithString:self.url];
if (self.isCancelled) return;
// 下面这行很耗时
NSData *data = [NSData dataWithContentsOfURL:imaUrl];
if (self.isCancelled) return;
UIImage *image = [UIImage imageWithData:data];
if (self.isCancelled) return;
// 返回主线程显示图片
// 通过代理
if ([self.delegate respondsToSelector:@selector(downloadOperation:didFinishedDownload:)]) {
[self.delegate downloadOperation:self didFinishedDownload:image];
}
}
}
@end
具体利用MVC模式创建的文件老生常谈,只来一个Controller.m文件供参考,数据存储存在问题,可以改用SDWebImage框架处理
//
// DYFTableViewController.m
// 624-05-自定义Operation
//
// Created by dyf on 14-6-24.
// Copyright (c) 2014年 ___FULLUSERNAME___. All rights reserved.
//
#import "DYFTableViewController.h"
#import "DYFAppModel.h"
#import "DYFDownloadOperation.h"
#warning Dictionary基础知识不太理解,字典的赋值回去看看笔记
@interface DYFTableViewController ()<DYFDownloadOperationDelegate>
@property (nonatomic, strong) NSArray *apps;
@property (nonatomic, strong) NSOperationQueue *queue;
/**
* key:url value:operation对象
*/
@property (nonatomic, strong) NSMutableDictionary *oprations;
/**
* key:url value:image对象
*/
@property (nonatomic, strong) NSMutableDictionary *images;
@end
@implementation DYFTableViewController
#pragma mark - 4个懒加载
- (NSArray *)apps
{
if (!_apps) {
NSString *path = [[NSBundle mainBundle] pathForResource:@"apps" ofType:@"plist"];
NSArray *arrayApps = [NSArray arrayWithContentsOfFile:path];
NSMutableArray *arrayM = [NSMutableArray arrayWithCapacity:arrayApps.count];
for (NSDictionary *dict in arrayApps) {
DYFAppModel *appM = [DYFAppModel appWithDict:dict];
[arrayM addObject:appM];
}
_apps = arrayM;
}
return _apps;
}
- (NSOperationQueue *)queue
{
if (!_queue) {
_queue = [[NSOperationQueue alloc] init];
// 设置最大并发线程数,最多同时下载3张图片
_queue.maxConcurrentOperationCount = 3;
}
return _queue;
}
- (NSMutableDictionary *)oprations
{
if (!_oprations) {
_oprations = [[NSMutableDictionary alloc] init];
}
return _oprations;
}
- (NSMutableDictionary *)images
{
if (!_images) {
_images = [NSMutableDictionary dictionary];
}
return _images;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
#pragma mark - 数据源方法
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.apps.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// 1.创建cell
static NSString *identifier = @"apps";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifier];
}
// 2.设置cell的数据
DYFAppModel *app = self.apps[indexPath.row];
cell.textLabel.text = app.name;
cell.detailTextLabel.text = app.download;
// 重点是如何从网络下载图片传入cell上面
// 每个url对应一个DYFDownloadOperation对象
// 每个url对应一个image对象
UIImage *image = self.images[app.icon];
if (image) {
// 若缓存中存在图片
cell.imageView.image = image;
}else
{
// 若缓存中不存在图片,则图片要从网上下载
// 设置下载前系统刷出的图片
cell.imageView.image = [UIImage imageNamed:@"身份证小"];
// 基础差,下面这行不太理解
DYFDownloadOperation *operation = self.oprations[app.icon];
if (operation) {
// 若正在下载,则不执行其它操作
}else
{
// 若没在下载,则创建开始下载的子线程
DYFDownloadOperation *operation = [[DYFDownloadOperation alloc] init];
operation.url = app.icon;
operation.indexPath = indexPath;
operation.delegate = self;
// 添加任务进队列,异步下载
[self.queue addOperation:operation];
// 基础差,下面这行不太理解
self.oprations[app.icon] = operation;
}
}
// 3.返回cell
return cell;
}
#pragma mark - DYFDownloadOperationDelegate
- (void)downloadOperation:(DYFDownloadOperation *)operation didFinishedDownload:(UIImage *)image
{
// 1.删除执行完毕的下载操作
[self.oprations removeObjectForKey:operation.url];
// 若图片下载好
if (image) {
// 2.将下载好的图片存入缓存
self.images[operation.url] = image;
// 3.刷新这一行cell的数据
[self.tableView reloadRowsAtIndexPaths:@[operation.indexPath] withRowAnimation:UITableViewRowAnimationNone];
// 4.将图片存入沙盒
// 4.1图片先转换为2进制数据
NSData *data = UIImagePNGRepresentation(image);
// 4.2设置沙盒路径
NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:[self.apps[operation.indexPath.row] icon]];
NSLog(@"%@", path);
// 4.3保存data到path中
[data writeToFile:path atomically:YES];
}
}
// 开始拖拽时候暂停队列
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
[self.queue setSuspended:YES];
}
// 停止拖拽的时候重启队列
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{
[self.queue setSuspended:NO];
}
@end