多线程一直是很操蛋的一个话题,其实如果你理解了很简单,没理解很麻烦;在OC中多线程通常有以下四种
四种线程 pthread_t ,NSThread, GCD,NSOperation+NSOperationQueue
第一种 pthread_t
严格的来说pthread_t并不仅仅属于OC的线程,但是OC牛逼的地方在于它完全兼容C和C++代码,这个玩意个人觉得只需要了解;毕竟这东西的确用的不多,而且相当蛋疼,我们用一个例子说明下这东西的用法 ,既然说了是C语言代码那么就要用C语言的代码搞一搞多线程了,OC需要使用直接复制过去就可以
#include <stdio.h>
#include <pthread.h>
void testThread();
void *start(void *data);
int main(int argc, char const *argv[])
{
puts("================ start =================");
testThread();
puts("================ end =================");
int i;
for(i=0;i<=10;i++){
printf("main === %d\n", i);
}
return 0;
}
void testThread(){
pthread_t pthread;
pthread_create(&pthread,NULL,start,NULL);
}
void *start(void *data){
puts("this is thread test !!");
int i;
for(i=0;i<=5;i++){
printf("start === %d\n", i);
}
return NULL;
}
OC中需要导入头文件,如上图
输出结果如下图
gcc 编译C文件这里就不多解释了,上面的结果说明了一切,至于其他的用法就不多解释了 ,以上就是所谓的 pthread_t的用法了,我们仅作为了解了
第二种 NSThread
NSThread 分为两种:一种是自启动,一种是手动启动;两种线程的子线程体都可以是block或者自定义方法
+ (void)detachNewThreadWithBlock:(void (^)(void))block;
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;
- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument;
- (instancetype)initWithBlock:(void (^)(void))block;
自启动:顾名思义就是创建这个线程就会自动执行线程的方法
手动启动:需要手动调用线程的start方法才可以启动
上代码,看看如何使用
-(void)testThreadBlock{
NSThread *thread = [[NSThread alloc] initWithBlock:^{
for (int i=0; i<=10; i++) {
NSLog(@"thread +++++ %d",i);
}
}];
[thread start];
[NSThread detachNewThreadWithBlock:^{
for (int i=0; i<=10; i++) {
NSLog(@"NSThread _____ %d",i);
}
}];
}
-(void)testThreadSelector{
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadAction1) object:nil];
[thread start];
[NSThread detachNewThreadSelector:@selector(threadAction2) toTarget:self withObject:nil];
}
-(void)threadAction1{
for (int i=0; i<=10; i++) {
NSLog(@"thread +++++ %d",i);
}
}
-(void)threadAction2{
for (int i=0; i<=10; i++) {
NSLog(@"NSThread _____ %d",i);
}
}
看下日志输出
在NSThread中有一个属性用的比较多 threadPriority 线程优先级属性,这个属性取值是从 0 到 1.0 ;数值越大优先级就越高,通常如果不设置这个值的话系统默认这个值是 0.5
如果把这个值越大:有更高的概率先执行,反之则先执行的概率较低我们比较下这是成0.1 和 0.9 比较下debug的日志
设置成0.1的时候,我们日志的前面大部分是main;当设置成0.9的是,日志前面大部分是thread
类方法和对象方法都可以设置 threadPriority
方法属性 | 类方法 | 对象方法 |
创建 | 类方法创建 | 对象方法创建 |
支持block | 支持 | 支持 |
支持SEL | 支持 | 支持 |
启动 | 自启动 | start方法启动 |
退出 | exit方法退出 | cancel方法退出 |
默认优先级 | 0.5 | 0.5 |
可设置优先级 | 可设置 | 可设置,可以单个线程设置 |
除此之外NSThread 对 NSObject进行了扩展,我们偶尔能用到的开一个新线程执行 和 放在后台执行;但是要注意的是延时执行是RunLoop相关的,但是我猜想跟线程应该有些关系。。。
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg ;
上面的两个方法其实是OC给我们提供了一种简单的开启新线程的方法,这里就不演示了,方法的名称说明了一切
第三种 GCD
首先GCD是啥子 ? 最好的解释是 基于C语言的一种比较装逼的高级的且适用于OC的多线程 我觉得你能够接触到多线程,想必对GCD会有一点了解 ;回到我们的源头,为什么要用多线程? 其实多线程无非就是用来解决 处理些长时间耗时的或者阻塞当前代码运行的 一种技术吧;比如说大文件下载,列表加载图片等等。。 讲到GCD之前,先引入一个实例
在这个页面要下载9张图片,如果使用多线程肯定会滑动卡顿的,可以这么理解:主线程负责UI跟用户的交互,子线程负责下载图片;当主线程去下载图片的时候就会影响用户交互也就是卡顿 —- 题外话 。。。
上面有9张图片在下载, 下载每一张图片的这个过程我们称之为任务 ,也就是上图有9个任务
那么上面的9个任务组成了一个任务组,我们称这个任务组叫队列
任务:耗时操作
队列:多个任务的集合
也就是GCD的操作是基于 队列和任务了,那么我们就来探讨下队列;队列是任务的容器,队列分为两种,有些地方也分为三种;串行队列,并行队列,主队列; ,任务也分为两种 : 同步任务 和 异步任务
- 串行队列 同步任务
- 串行队列 异步任务
- 并行队列 同步任务
- 并行队列 异步任务
- 主队列 同步任务
- 主队列 异步任务
1 串行队列 同步任务
串行队列创建
-(void)queueTest1{
NSLog(@"currentThread ====== %ld",[NSThread currentThread].hash);
NSLog(@"mainThread ====== %ld",[NSThread mainThread].hash);
dispatch_sync(queue, ^{
NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:@"https://hebiao.online/img/a1.jpg"]];
UIImage *img = [[UIImage alloc] initWithData:data];
NSLog(@"dispatch_sync1 ======= %ld imagesize = %@",[NSThread currentThread].hash ,NSStringFromCGSize(img.size));
});
dispatch_sync(queue, ^{
NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:@"https://hebiao.online/img/a2.jpg"]];
UIImage *img = [[UIImage alloc] initWithData:data];
NSLog(@"dispatch_sync2 ======= %ld imagesize = %@",[NSThread currentThread].hash ,NSStringFromCGSize(img.size));
NSLog(@"after download ====== %ld",[NSThread currentThread].hash);
}
队列创建包含两个参数
第一个是队列的标识 “hebiao.queue” 自定义标识
第二个是串行队列的标志 DISPATCH_QUEUE_SERIAL 系统常量
dispatch_queue_t queue = dispatch_queue_create("hebiao.queue", DISPATCH_QUEUE_SERIAL);
创建一个串行队列
dispatch_sync(queue, ^{
});
创建一个同步任务 , 如何知道是同步任务还是 异步任务了 ? 我们看下日志输出
2017-08-30 09:33:15.677615+0800 TestNSProxy[9889:3274457] currentThread ====== 6174428672
2017-08-30 09:33:15.677692+0800 TestNSProxy[9889:3274457] mainThread ====== 6174428672
2017-08-30 09:33:23.107414+0800 TestNSProxy[9889:3274457] dispatch_sync1 ======= 6174428672 imagesize = {2592, 1552}
2017-08-30 09:33:23.226778+0800 TestNSProxy[9889:3274457] dispatch_sync2 ======= 6174428672 imagesize = {500, 313}
2017-08-30 09:33:23.226852+0800 TestNSProxy[9889:3274457] after download ====== 6174428672
我们发现线程的hash值是一样的然后 下载图片算是一个耗时操作并且第一张图片下载完成之后就下载第二张,按照代码的顺序所以我们可以得出结论如下 串行队列 同步任务: 不开启新的线程,按队列的顺序执行
2 串行队列 异步任务
串行队列创建 跟上面是一样的 。。
-(void)queueTest2{
NSLog(@"currentThread ====== %ld",[NSThread currentThread].hash);
NSLog(@"mainThread ====== %ld",[NSThread mainThread].hash);
dispatch_queue_t queue = dispatch_queue_create("hebiao.queue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:@"https://hebiao.online/img/a1.jpg"]];
UIImage *img = [[UIImage alloc] initWithData:data];
NSLog(@"dispatch_async1 ======= %ld imagesize = %@",[NSThread currentThread].hash ,NSStringFromCGSize(img.size));
});
dispatch_async(queue, ^{
NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:@"https://hebiao.online/img/a2.jpg"]];
UIImage *img = [[UIImage alloc] initWithData:data];
NSLog(@"dispatch_async2 ======= %ld imagesize = %@",[NSThread currentThread].hash ,NSStringFromCGSize(img.size));
});
NSLog(@"after download ====== %ld",[NSThread currentThread].hash);
}
队列创建包含两个参数
第一个是队列的标识 “hebiao.queue” 自定义标识
第二个是串行队列的标志 DISPATCH_QUEUE_SERIAL 系统常量
dispatch_queue_t queue = dispatch_queue_create("hebiao.queue", DISPATCH_QUEUE_SERIAL);
创建一个串行队列 , 这个跟上面是一样的 ,不同的是异步任务
dispatch_async(queue, ^{
});
创建一个异步任务; 我们发现 这两个很像 同步任务dispatch_sync 和异步任务dispatch_async 就差一个字母 a 看下日志
2017-08-30 09:44:31.416907+0800 TestNSProxy[9893:3277114] currentThread ====== 6241615616
2017-08-30 09:44:31.416957+0800 TestNSProxy[9893:3277114] mainThread ====== 6241615616
2017-08-30 09:44:31.417000+0800 TestNSProxy[9893:3277114] after download ====== 6241615616
2017-08-30 09:44:42.403647+0800 TestNSProxy[9893:3277155] dispatch_async1 ======= 6243653760 imagesize = {2592, 1552}
2017-08-30 09:44:42.530342+0800 TestNSProxy[9893:3277155] dispatch_async2 ======= 6243653760 imagesize = {500, 313}
我们异步任务中线程的hash值和主线程不一样,然后 下载图片算是一个耗时操作并且第一张图片下载完成之后就下载第二张,按照代码的顺序执行,所以我们可以得出结论如下 串行队列 异步步任务: 为所有任务开启一个新的线程,按队列的顺序执行
3 并行队列 同步任务
串行队列和并行队列关键字修饰不一样 ,直接上代码
-(void)queueTest3{
NSLog(@"currentThread ====== %ld",[NSThread currentThread].hash);
NSLog(@"mainThread ====== %ld",[NSThread mainThread].hash);
dispatch_queue_t queue = dispatch_queue_create("hebiao.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:@"https://hebiao.online/img/a1.jpg"]];
UIImage *img = [[UIImage alloc] initWithData:data];
NSLog(@"dispatch_sync1 ======= %ld imagesize = %@",[NSThread currentThread].hash ,NSStringFromCGSize(img.size));
});
dispatch_sync(queue, ^{
NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:@"https://hebiao.online/img/tomcat.png"]];
UIImage *img = [[UIImage alloc] initWithData:data];
NSLog(@"dispatch_sync2 ======= %ld imagesize = %@",[NSThread currentThread].hash ,NSStringFromCGSize(img.size));
});
NSLog(@"after download ====== %ld",[NSThread currentThread].hash);
}
队列创建包含两个参数
第一个是队列的标识 “hebiao.queue” 自定义标识
第二个是并行队列的标志 DISPATCH_QUEUE_CONCURRENT 系统常量
dispatch_queue_t queue = dispatch_queue_create("hebiao.queue", DISPATCH_QUEUE_CONCURRENT);
创建一个串行队列
dispatch_sync(queue, ^{
});
创建一个同步任务 我们看下日志输出
2017-08-30 09:55:27.481648+0800 TestNSProxy[9905:3280774] currentThread ====== 6174477056
2017-08-30 09:55:27.481706+0800 TestNSProxy[9905:3280774] mainThread ====== 6174477056
2017-08-30 09:55:35.257421+0800 TestNSProxy[9905:3280774] dispatch_sync1 ======= 6174477056 imagesize = {2592, 1552}
2017-08-30 09:55:35.295198+0800 TestNSProxy[9905:3280774] dispatch_sync2 ======= 6174477056 imagesize = {146, 92}
2017-08-30 09:55:35.295324+0800 TestNSProxy[9905:3280774] after download ====== 6174477056
我们发现只要是同步任务,不管是并行队列和串行队列 输出是一样的 ,不开启新线程并且顺序执行 我们可以得出结论如下 并行队列 同步任务: 不开启新的线程,按队列的顺序执行
4 并行队列 异步任务
直接上代码 。。
-(void)queueTest4{
NSLog(@"currentThread ====== %ld",[NSThread currentThread].hash);
NSLog(@"mainThread ====== %ld",[NSThread mainThread].hash);
dispatch_queue_t queue = dispatch_queue_create("hebiao.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:@"https://hebiao.online/img/a1.jpg"]];
UIImage *img = [[UIImage alloc] initWithData:data];
NSLog(@"dispatch_async1 ======= %ld imagesize = %@",[NSThread currentThread].hash ,NSStringFromCGSize(img.size));
});
dispatch_async(queue, ^{
NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:@"https://hebiao.online/img/tomcat.png"]];
UIImage *img = [[UIImage alloc] initWithData:data];
NSLog(@"dispatch_async2 ======= %ld imagesize = %@",[NSThread currentThread].hash ,NSStringFromCGSize(img.size));
});
NSLog(@"after download ====== %ld",[NSThread currentThread].hash);
}
我们直接输出日志,在输出日志之前先申明下 https://hebiao.online/img/a1.jpg 这个图片较大, 而https://hebiao.online/img/tomcat.png这个图片很小 ,我们看下日志
2017-08-30 10:05:52.386516+0800 TestNSProxy[9909:3283332] currentThread ====== 6174457024
2017-08-30 10:05:52.386569+0800 TestNSProxy[9909:3283332] mainThread ====== 6174457024
2017-08-30 10:05:52.386616+0800 TestNSProxy[9909:3283332] after download ====== 6174457024
2017-08-30 10:05:52.845416+0800 TestNSProxy[9909:3283372] dispatch_async2 ======= 6174523776 imagesize = {146, 92}
2017-08-30 10:05:59.969279+0800 TestNSProxy[9909:3283369] dispatch_async1 ======= 6241632640 imagesize = {2592, 1552}
我们发现 ,有三个不同的线程hash值;也就是除了主线程之外分别为两个任务开启了两个子线程,并且不是按照顺序执行的,而且我们发现图片小的先下载完成,总结下并行队列 异步步任务: 为每个任务开启新的线程,不按队列的顺序执行
5 主队列 同步任务
上代码
-(void)queueTest5{
NSLog(@"currentThread ====== %ld",[NSThread currentThread].hash);
NSLog(@"mainThread ====== %ld",[NSThread mainThread].hash);
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:@"https://hebiao.online/img/a1.jpg"]];
UIImage *img = [[UIImage alloc] initWithData:data];
NSLog(@"dispatch_sync1 ======= %ld imagesize = %@",[NSThread currentThread].hash ,NSStringFromCGSize(img.size));
});
dispatch_sync(queue, ^{
NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:@"https://hebiao.online/img/tomcat.png"]];
UIImage *img = [[UIImage alloc] initWithData:data];
NSLog(@"dispatch_sync2 ======= %ld imagesize = %@",[NSThread currentThread].hash ,NSStringFromCGSize(img.size));
});
NSLog(@"after download ====== %ld",[NSThread currentThread].hash);
}
创建主队列
dispatch_queue_t queue = dispatch_get_main_queue();
其他的地方是一样的,但是执行的时候就报错了 。。。。。
总结下主队列 同步任务: 直接报错
6 主队列 异步任务
上代码
-(void)queueTest6{
NSLog(@"currentThread ====== %ld",[NSThread currentThread].hash);
NSLog(@"mainThread ====== %ld",[NSThread mainThread].hash);
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:@"https://hebiao.online/img/a1.jpg"]];
UIImage *img = [[UIImage alloc] initWithData:data];
NSLog(@"dispatch_async1 ======= %ld imagesize = %@",[NSThread currentThread].hash ,NSStringFromCGSize(img.size));
});
dispatch_async(queue, ^{
NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:@"https://hebiao.online/img/tomcat.png"]];
UIImage *img = [[UIImage alloc] initWithData:data];
NSLog(@"dispatch_async2 ======= %ld imagesize = %@",[NSThread currentThread].hash ,NSStringFromCGSize(img.size));
});
NSLog(@"after download ====== %ld",[NSThread currentThread].hash);
}
跟上面类似,只是同步和异步的区别 ,然后看下日志的输出
2017-08-30 10:29:50.184149+0800 TestNSProxy[9936:3290719] currentThread ====== 6174437632
2017-08-30 10:29:50.184198+0800 TestNSProxy[9936:3290719] mainThread ====== 6174437632
2017-08-30 10:29:50.184225+0800 TestNSProxy[9936:3290719] after download ====== 6174437632
2017-08-30 10:30:00.720892+0800 TestNSProxy[9936:3290719] dispatch_async1 ======= 6174437632 imagesize = {2592, 1552}
2017-08-30 10:30:00.759890+0800 TestNSProxy[9936:3290719] dispatch_async2 ======= 6174437632 imagesize = {146, 92}
看日志我们发现,线程hash值是一样的,但是执行的顺序不一样 总结下主队列 异步任务: 不开启新的线程,不按队列的顺序执行
总结下以上几种队列
队列 | 同步任务 | 异步任务 |
串行队列 | 不开新线程,顺序执行 | 为任务开启一个新线程,顺序执行 |
并行队列 | 不开新线程,顺序执行 | 为每个任务开启一个新线程,不按顺序执行 |
主队列 | 无效报错 | 不开新线程 ,不按顺序执行 |
结论
同步任务:不开启新线程,顺序执行
异步任务:主队列不开新线程
第四种 NSOperation+NSOperationQueue
NSOperation 按照GCD的理解就是任务
NSOperationQueue 同样按照GCD的理解就是队列
NSOperation 有两个子类 NSBlockOperation 和 NSInvocationOperation 通过名称不难看出,一个是block方法,一个是SEL方法;我们一般使用的是NSOperation的子类或者自定义一个类继承NSOperation 跟NSThread是类似的 ,分别来看下用法
1 NSBlockOperation
-(void)queueTest7{
NSLog(@"currentThread ====== %ld",[NSThread currentThread].hash);
NSLog(@"mainThread ====== %ld",[NSThread mainThread].hash);
// NSBlockOperation *operatiion = [[NSBlockOperation alloc] init]; 两种都一样
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"operation1 ====== %ld",[NSThread currentThread].hash);
}];
[operation addExecutionBlock:^{
NSLog(@"operation2 ====== %ld",[NSThread currentThread].hash);
}];
[operation start];
}
2017-08-30 11:28:55.177034+0800 TestNSProxy[9955:3301574] currentThread ====== 6241573952
2017-08-30 11:28:55.177090+0800 TestNSProxy[9955:3301574] mainThread ====== 6241573952
2017-08-30 11:28:55.177263+0800 TestNSProxy[9955:3301617] operation1 ====== 6174533376
2017-08-30 11:28:55.177277+0800 TestNSProxy[9955:3301617] operation2 ====== 6174533376
单纯的 NSBlockOperation 会开启一个新线程并且按顺序执行; 跟我们上面提到的 串行队列 异步任务
2 NSInvocationOperation
先上代码
-(void)queueTest8{
NSLog(@"currentThread ====== %ld",[NSThread currentThread].hash);
NSLog(@"mainThread ====== %ld",[NSThread mainThread].hash);
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(action:) object:NULL];
[operation start];
}
-(void)action:(id)sender{
NSLog(@"operation ====== %ld",[NSThread currentThread].hash);
}
2017-08-30 11:48:05.237857+0800 TestNSProxy[9965:3306128] currentThread ====== 6174532032
2017-08-30 11:48:05.237909+0800 TestNSProxy[9965:3306128] mainThread ====== 6174532032
2017-08-30 11:48:05.238099+0800 TestNSProxy[9965:3306128] operation ====== 6174532032
这个类很尴尬 , 只有一个所谓的任务,而且没有开启新的线程
3 NSOperationQueue
会有这么一个问题,如果我想搞一个 并行队列 异步任务 了 ? 按照上面的方式貌似没有什么搞头 ,那么 。。。 就要引入 NSOperationQueue ,先来看下NSOperationQueue的初始化
-(void)queueTest9{
NSLog(@"currentThread ====== %ld",[NSThread currentThread].hash);
NSLog(@"mainThread ====== %ld",[NSThread mainThread].hash);
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperationWithBlock:^{
NSLog(@"queue ====== %ld",[NSThread currentThread].hash);
}];
NSOperationQueue *mqueue = [NSOperationQueue mainQueue];
[mqueue addOperationWithBlock:^{
NSLog(@"mqueue ====== %ld",[NSThread currentThread].hash);
}];
}
2017-08-30 14:10:29.330361+0800 TestNSProxy[10105:3345221] currentThread ====== 6174459328
2017-08-30 14:10:29.330427+0800 TestNSProxy[10105:3345221] mainThread ====== 6174459328
2017-08-30 14:10:29.331572+0800 TestNSProxy[10105:3345270] queue ====== 6241580672
2017-08-30 14:10:29.336198+0800 TestNSProxy[10105:3345221] mqueue ====== 6174459328
NSOperationQueue 初始化有方法有两种,一种是 alloc 一种是 mainQueue ;我们发现 alloc创建的queue是开启了一个新的线程,而mainQueue 并没有开启新线程 ;还有 一点比较重要的是 NSOperationQueue 无需手动启动
我们可以把NSOperationQueue 跟前面的 NSOperation 关联起来
-(void)queueTest10{
NSLog(@"currentThread ====== %ld",[NSThread currentThread].hash);
NSLog(@"mainThread ====== %ld",[NSThread mainThread].hash);
NSBlockOperation *blockOperation1 = [[NSBlockOperation alloc] init];
[blockOperation1 addExecutionBlock:^{
NSLog(@"blockOperation1 ======= %ld ",[NSThread currentThread].hash);
}];
NSInvocationOperation *blockOperation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(opAction:) object:nil];
NSLog(@" === over === ");
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:blockOperation1];
[queue addOperation:blockOperation2];
}
-(void)opAction:(id)sender{
NSLog(@"blockOperation2 ======= %ld ",[NSThread currentThread].hash );
}
2017-08-30 14:33:28.151707+0800 TestNSProxy[10122:3352107] currentThread ====== 6174484288
2017-08-30 14:33:28.151762+0800 TestNSProxy[10122:3352107] mainThread ====== 6174484288
2017-08-30 14:33:28.152059+0800 TestNSProxy[10122:3352107] === over ===
2017-08-30 14:33:28.152201+0800 TestNSProxy[10122:3352159] blockOperation1 ======= 6176507712
2017-08-30 14:33:28.152299+0800 TestNSProxy[10122:3352159] blockOperation2 ======= 6176507712
通过上面的代码我们把NSOperationQueue 和 NSOperation 联系在一起了,但是这貌似没有跟GCD相比少点什么 。。。 如何让多个任务同时执行了 ?
-(void)queueTest11{
NSLog(@"currentThread ====== %ld",[NSThread currentThread].hash);
NSLog(@"mainThread ====== %ld",[NSThread mainThread].hash);
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 1;
[queue addOperationWithBlock:^{
NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:@"https://hebiao.online/img/a1.jpg"]];
UIImage *img = [[UIImage alloc] initWithData:data];
NSLog(@"queue1 ======= %ld imagesize = %@",[NSThread currentThread].hash ,NSStringFromCGSize(img.size));
}];
[queue addOperationWithBlock:^{
NSLog(@"queue2 ====== %ld",[NSThread currentThread].hash);
}];
[queue addOperationWithBlock:^{
NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:@"https://hebiao.online/img/tomcat.png"]];
UIImage *img = [[UIImage alloc] initWithData:data];
NSLog(@"queue3 ======= %ld imagesize = %@",[NSThread currentThread].hash ,NSStringFromCGSize(img.size));
}];
[queue addOperationWithBlock:^{
NSLog(@"queue4 ====== %ld",[NSThread currentThread].hash);
}];
[queue addOperationWithBlock:^{
NSLog(@"queue5 ====== %ld",[NSThread currentThread].hash);
}];
NSLog(@" === over === ");
}
2017-08-30 14:45:24.603104+0800 TestNSProxy[10134:3356916] currentThread ====== 6241543232
2017-08-30 14:45:24.603159+0800 TestNSProxy[10134:3356916] mainThread ====== 6241543232
2017-08-30 14:45:24.603443+0800 TestNSProxy[10134:3356916] === over ===
2017-08-30 14:45:32.057217+0800 TestNSProxy[10134:3356962] queue1 ======= 6174480896 imagesize = {2592, 1552}
2017-08-30 14:45:32.058261+0800 TestNSProxy[10134:3356963] queue2 ====== 6174466304
2017-08-30 14:45:32.150285+0800 TestNSProxy[10134:3356963] queue3 ======= 6174466304 imagesize = {146, 92}
2017-08-30 14:45:32.150746+0800 TestNSProxy[10134:3356963] queue4 ====== 6174466304
2017-08-30 14:45:32.150973+0800 TestNSProxy[10134:3356963] queue5 ====== 6174466304
NSOperationQueue 提供了 一个 maxConcurrentOperationCount 最大线程数量;我们可以这么理解,除了主线程之外,最多可以开启多少个任务数量,有点像迅雷下载 。。。。 上面的任务数设置为1,但是看到上面的日志 ,我觉得这个说法还得加个修饰 同时开启的最大线程数量
当我把上面的数值调整的时候
queue.maxConcurrentOperationCount = 5;
2017-08-30 14:48:43.145571+0800 TestNSProxy[10138:3358375] currentThread ====== 6176514752
2017-08-30 14:48:43.145625+0800 TestNSProxy[10138:3358375] mainThread ====== 6176514752
2017-08-30 14:48:43.145863+0800 TestNSProxy[10138:3358375] === over ===
2017-08-30 14:48:43.146940+0800 TestNSProxy[10138:3358423] queue2 ====== 6176556160
2017-08-30 14:48:43.150508+0800 TestNSProxy[10138:3358422] queue4 ====== 6176549760
2017-08-30 14:48:43.150566+0800 TestNSProxy[10138:3358422] queue5 ====== 6176549760
2017-08-30 14:48:44.280059+0800 TestNSProxy[10138:3358423] queue3 ======= 6176556160 imagesize = {146, 92}
2017-08-30 14:48:50.769027+0800 TestNSProxy[10138:3358424] queue1 ======= 6176593088 imagesize = {2592, 1552}
我们发现得到的输出顺序变了 。。。。 我们甚至可以得出这样的一个结论
queue.maxConcurrentOperationCount = 1 的时候 NSOperationQueue 为串行队列
而当maxConcurrentOperationCount > 1 的时候 NSOperationQueue 并行队列 ;
并且 maxConcurrentOperationCount 默认为 1
另外NSOperation 还有一个比较常用的功能就是 NSOperation 是可以相互依赖的,有点像Jar包依赖;举个简单的例子,我要吃饭,但是我必须用碗把饭先盛着再吃,把饭盛到碗了这个动作可以理解为依赖
没有依赖之前
-(void)queueTest12{
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"operation1 ====== %ld",[NSThread currentThread].hash);
}];
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"operation2 ====== %ld",[NSThread currentThread].hash);
}];
NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"operation3 ====== %ld",[NSThread currentThread].hash);
}];
NSBlockOperation *operation4 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"operation4 ====== %ld",[NSThread currentThread].hash);
}];
NSBlockOperation *operation5 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"operation5 ====== %ld",[NSThread currentThread].hash);
}];
[queue addOperation:operation1];
[queue addOperation:operation2];
[queue addOperation:operation3];
[queue addOperation:operation4];
[queue addOperation:operation5];
}
2017-08-30 15:05:58.261291+0800 TestNSProxy[10151:3363635] operation2 ====== 6176545728
2017-08-30 15:05:58.261399+0800 TestNSProxy[10151:3363631] operation1 ====== 6243625856
2017-08-30 15:05:58.261440+0800 TestNSProxy[10151:3363635] operation3 ====== 6176545728
2017-08-30 15:05:58.261504+0800 TestNSProxy[10151:3363631] operation4 ====== 6243625856
2017-08-30 15:05:58.261524+0800 TestNSProxy[10151:3363635] operation5 ====== 6176545728
1-5是顺序执行的 , 一旦加入了依赖 就变化 了 。。
-(void)queueTest12{
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"operation1 ====== %ld",[NSThread currentThread].hash);
}];
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"operation2 ====== %ld",[NSThread currentThread].hash);
}];
NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"operation3 ====== %ld",[NSThread currentThread].hash);
}];
NSBlockOperation *operation4 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"operation4 ====== %ld",[NSThread currentThread].hash);
}];
NSBlockOperation *operation5 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"operation5 ====== %ld",[NSThread currentThread].hash);
}];
[operation1 addDependency:operation2];
[operation2 addDependency:operation3];
[operation3 addDependency:operation4];
[operation4 addDependency:operation5];
[queue addOperation:operation1];
[queue addOperation:operation2];
[queue addOperation:operation3];
[queue addOperation:operation4];
[queue addOperation:operation5];
}
2017-08-30 15:07:55.438561+0800 TestNSProxy[10153:3364304] operation5 ====== 6241643648
2017-08-30 15:07:55.438650+0800 TestNSProxy[10153:3364304] operation4 ====== 6241643648
2017-08-30 15:07:55.438685+0800 TestNSProxy[10153:3364304] operation3 ====== 6241643648
2017-08-30 15:07:55.438714+0800 TestNSProxy[10153:3364304] operation2 ====== 6241643648
2017-08-30 15:07:55.438743+0800 TestNSProxy[10153:3364304] operation1 ====== 6241643648
顺序变了 , 优先执行 被依赖的 NSBlockOperation。。。。。。
兵无常势 水无常形 具体的项目具体操作 。。