首页 > 代码库 > iOS开发多线程基础知识 NSOperation

iOS开发多线程基础知识 NSOperation

-------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 = http://www.mamicode.com/UIImagePNGRepresentation(image);"%@", 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