上篇转载的博文有讲解多线程的几种使用,但是NSOperation的介绍不够详细,在此做一下补充。
NSOperation本身是不能直接用来操作的,需要继承NSOperation实现相应的方法或者直接使用NSBlockOperation、NSInvocationOperation才能正常使用。下面就分别从自定义、NSBlockOperation、NSInvocationOperation三个方面来讲解:
1、自定义
//
// PHOperation.h
// OperationTest
//
// Created by 項普華 on 2017/2/9.
// 邮箱: xiangpuhua@126.com
// 电话: +86 13316987488
// 主页: https://github.com/xphaijj
// Copyright © 2017年 項普華. All rights reserved.
//
#import <Foundation/Foundation.h>
typedef void(^DownloadBlock)(id file);
@interface PHOperation : NSOperation
/**
* 线程执行下载任务
*
* @param urlString 需要下载的地址
* @param downloadBlock 下载完成的回调
*
* @return 当前类的实例
*/
- (id)initWithDownloadURL:(NSString *)urlString
block:(DownloadBlock)downloadBlock;
@end
//
// PHOperation.m
// OperationTest
//
// Created by 項普華 on 2017/2/9.
// 邮箱: xiangpuhua@126.com
// 电话: +86 13316987488
// 主页: https://github.com/xphaijj
// Copyright © 2017年 項普華. All rights reserved.
//
#import "PHOperation.h"
@interface PHOperation () {
}
@property (nonatomic, strong) NSString *urlStr;
@property (nonatomic, copy) DownloadBlock block;
@end
@implementation PHOperation
/**
* 线程执行下载任务
*
* @param urlString 需要下载的地址
* @param downloadBlock 下载完成的回调
*
* @return 当前类的实例
*/
- (id)initWithDownloadURL:(NSString *)urlString
block:(DownloadBlock)downloadBlock {
self = [super init];
if (self) {
_urlStr = urlString;
_block = downloadBlock;
}
return self;
}
/**
* 自定义main方法执行线程任务
*/
- (void)main {
@try {
@autoreleasepool {
if (self.isCancelled) {
return;
}
NSURL *url = [NSURL URLWithString:self.urlStr];
NSData *data = [NSData dataWithContentsOfURL:url];
if (self.isCancelled) {
url = nil;
data = nil;
return;
}
if (self.block) {
self.block(data);
}
}
} @catch (NSException *exception) {
} @finally {
}
}
@end
同步引用方法如下:
NSLog(@"start");
PHOperation *op = [[PHOperation alloc] initWithDownloadURL:@"https://www.lsd1888.com/res/21/57e77030f3d1d.jpg" block:^(id file) {
sleep(4);
UIImage *image = [UIImage imageWithData:file];
NSLog(@"%@ %@", image, [NSThread currentThread]);
}];
[op start];//执行同步操作
NSLog(@"end");
输出结果:
2017-02-09 12:31:41.320 OperationTest[4553:996304] start
2017-02-09 12:31:45.730 OperationTest[4553:996304] , {320, 353} {number = 1, name = main}
2017-02-09 12:31:45.730 OperationTest[4553:996304] end
由输出结果我们可知,同步引用正常调起。
异步引用方法如下:
NSLog(@"start");
NSOperationQueue *phQueue = [[NSOperationQueue alloc] init];
PHOperation *op = [[PHOperation alloc] initWithDownloadURL:@"https://www.lsd1888.com/res/21/57e77030f3d1d.jpg" block:^(id file) {
sleep(4);
UIImage *image = [UIImage imageWithData:file];
NSLog(@"%@ %@", image, [NSThread currentThread]);
}];
[phQueue addOperation:op];//执行异步操作
NSLog(@"end");
输出结果:
2017-02-09 12:28:57.248 OperationTest[4493:979978] start
2017-02-09 12:28:57.249 OperationTest[4493:979978] end
2017-02-09 12:29:01.601 OperationTest[4493:980384] , {320, 353} {number = 5, name = (null)}
由输出结果我们可知异步引用正常调起。
NSLog(@"start");
PHOperation *op = [[PHOperation alloc] initWithDownloadURL:@"https://www.lsd1888.com/res/21/57e77030f3d1d.jpg" block:^(id file) {
sleep(4);
UIImage *image = [UIImage imageWithData:file];
NSLog(@"1 %@ %@", image, [NSThread currentThread]);
}];
PHOperation *op1 = [[PHOperation alloc] initWithDownloadURL:@"https://www.lsd1888.com/res/21/57e77030f3d1d.jpg" block:^(id file) {
sleep(2);
UIImage *image = [UIImage imageWithData:file];
NSLog(@"2 %@ %@", image, [NSThread currentThread]);
}];
[op start];
[op1 start];
NSLog(@"end");
输出结果:
2017-02-09 13:09:52.308 OperationTest[5420:1208386] start
2017-02-09 13:09:56.622 OperationTest[5420:1208386] 1 , {320, 353} {number = 1, name = main}
2017-02-09 13:09:58.852 OperationTest[5420:1208386] 2 , {320, 353} {number = 1, name = main}
2017-02-09 13:09:58.853 OperationTest[5420:1208386] end
由输出结果可知,op与op1执行了同步。
NSLog(@"start");
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
PHOperation *op = [[PHOperation alloc] initWithDownloadURL:@"https://www.lsd1888.com/res/21/57e77030f3d1d.jpg" block:^(id file) {
sleep(4);
UIImage *image = [UIImage imageWithData:file];
NSLog(@"1 %@ %@", image, [NSThread currentThread]);
}];
PHOperation *op1 = [[PHOperation alloc] initWithDownloadURL:@"https://www.lsd1888.com/res/21/57e77030f3d1d.jpg" block:^(id file) {
sleep(2);
UIImage *image = [UIImage imageWithData:file];
NSLog(@"2 %@ %@", image, [NSThread currentThread]);
}];
[queue addOperation:op];
[queue addOperation:op1];
NSLog(@"end");
输出结果:
2017-02-09 13:13:20.089 OperationTest[5510:1234538] start
2017-02-09 13:13:20.090 OperationTest[5510:1234538] end
2017-02-09 13:13:22.440 OperationTest[5510:1234706] 2 , {320, 353} {number = 5, name = (null)}
2017-02-09 13:13:24.534 OperationTest[5510:1234688] 1 , {320, 353} {number = 6, name = (null)}
由输出结果可知:op与op1之间执行了的是异步。
2、NSBlockOperation
NSLog(@"start");
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
sleep(4);
NSLog(@"op1");
}];
[op1 start];//同步执行
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"op2");
}];
[op2 start];//同步执行
NSLog(@"end");
输出结果:
2017-02-09 12:46:57.590 OperationTest[4835:1073639] start
2017-02-09 12:47:01.684 OperationTest[4835:1073639] op1
2017-02-09 12:47:01.684 OperationTest[4835:1073639] op2
2017-02-09 12:47:01.685 OperationTest[4835:1073639] end
由输出结果可知,以上方法均同步执行。
NSLog(@"start");
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
sleep(4);
NSLog(@"op1");
}];
[op1 start];//同步执行
NSBlockOperation *op2 = [[NSBlockOperation alloc] init];
[op2 addExecutionBlock:^{//异步操作
sleep(3);
NSLog(@"start");
}];
[op2 addExecutionBlock:^{
sleep(1);
NSLog(@"middle");
}];
[op2 addExecutionBlock:^{
sleep(2);
NSLog(@"end1");
}];
[op2 setCompletionBlock:^{
NSLog(@"op2 done");
}];
[op2 start];//同步执行
NSLog(@"end");
输出结果:
2017-02-09 12:51:41.925 OperationTest[4975:1102448] start
2017-02-09 12:51:45.997 OperationTest[4975:1102448] op1
2017-02-09 12:51:47.072 OperationTest[4975:1102542] middle
2017-02-09 12:51:48.070 OperationTest[4975:1102539] end1
2017-02-09 12:51:49.073 OperationTest[4975:1102448] start
2017-02-09 12:51:49.073 OperationTest[4975:1102448] end
2017-02-09 12:51:49.073 OperationTest[4975:1102539] op2 done
由上述执行结果可知:op1与op2之间同步执行,op2内部是异步执行。
NSLog(@"start");
NSOperationQueue *queueBlock = [[NSOperationQueue alloc] init];
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
sleep(2);
NSLog(@"op1");
}];
NSBlockOperation *op2 = [[NSBlockOperation alloc] init];
[op2 addExecutionBlock:^{//异步操作
sleep(3);
NSLog(@"start");
}];
[op2 addExecutionBlock:^{
sleep(1);
NSLog(@"middle");
}];
[op2 addExecutionBlock:^{
sleep(2);
NSLog(@"end1");
}];
[op2 setCompletionBlock:^{
NSLog(@"op2 done");
}];
[queueBlock addOperation:op1];
[queueBlock addOperation:op2];
NSLog(@"end");
执行结果:
2017-02-09 12:54:27.294 OperationTest[5105:1121668] start
2017-02-09 12:54:27.295 OperationTest[5105:1121668] end
2017-02-09 12:54:28.368 OperationTest[5105:1122096] middle
2017-02-09 12:54:29.368 OperationTest[5105:1122113] op1
2017-02-09 12:54:29.369 OperationTest[5105:1122086] end1
2017-02-09 12:54:30.370 OperationTest[5105:1122090] start
2017-02-09 12:54:30.370 OperationTest[5105:1122113] op2 done
由输出结果可知:op1与op2之间异步执行,op2内部异步执行。
3、NSInvocationOperation
NSLog(@"start");
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doPo:) object:@"1"];
NSInvocationOperation *operation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doPo:) object:@"2"];
[operation start];
[operation2 start];
NSLog(@"end");
输出结果:
2017-02-09 12:58:43.835 OperationTest[5209:1144896] start
2017-02-09 12:58:47.909 OperationTest[5209:1144896] 休眠4s后 输出参数:1
2017-02-09 12:58:51.984 OperationTest[5209:1144896] 休眠4s后 输出参数:2
2017-02-09 12:58:51.985 OperationTest[5209:1144896] end
根据输出结果可知,operation与operation2同步执行。
NSLog(@"start");
NSOperationQueue *queue = [[NSOperationQueue alloc] init];//直接是异步操作
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doPo:) object:@"4"];
NSInvocationOperation *operation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doPo:) object:@"3"];
[queue addOperation:operation];
[queue addOperation:operation2];
NSLog(@"end");
输出结果:
2017-02-09 13:01:19.211 OperationTest[5275:1160990] start
2017-02-09 13:01:19.213 OperationTest[5275:1160990] end
2017-02-09 13:01:22.287 OperationTest[5275:1161244] 休眠3s后 输出参数:3
2017-02-09 13:01:23.288 OperationTest[5275:1161249] 休眠4s后 输出参数:4
根据输出结果可知:operation与operation2之间异步执行。
综上所述:实现NSOperation的子类,实例本身调用start方法即执行同步方法,而加入执行队列以后执行异步方法。
注意:
GCD执行任务的方法并非只有简单的同步调用方法和异步调用方法,还有其他一些常用方法:
dispatch_apply():重复执行某个任务,但是注意这个方法没有办法异步执行(为了不阻塞线程可以使用dispatch_async()包装一下再执行)。
dispatch_once():单次执行一个任务,此方法中的任务只会执行一次,重复调用也没办法重复执行(单例模式中常用此方法)。
dispatch_time():延迟一定的时间后执行。
dispatch_barrier_async():使用此方法创建的任务首先会查看队列中有没有别的任务要执行,如果有,则会等待已有任务执行完毕再执行;同时在此方法后添加的任务必须等待此方法中任务执行后才能执行。(利用这个方法可以控制执行顺序,例如前面先加载最后一张图片的需求就可以先使用这个方法将最后一张图片加载的操作添加到队列,然后调用dispatch_async()添加其他图片加载任务)
dispatch_group_async():实现对任务分组管理,如果一组任务全部完成可以通过dispatch_group_notify()方法获得完成通知(需要定义dispatch_group_t作为分组标识)。