前序:按顺序阅读更好编程
iOS 多线程之GCDmarkdown
NSOperation是对GCD的包装,GCD只支持FIFO的队列,而NSOpration能够设置最大并发数、设置优先级、添加依赖关系等调整执行顺序,NSOpration甚至能够跨队列设置依赖关系多线程
NSOperatio有2个核心概念:NSOperation(操做)和NSOperationQueue(队列). NSOperation是个抽象类,依赖于子类NSInvocationOperation、NSBlockOperation去实现,另外还能够自定义NSOperation.并发
① 基本使用异步
- (void)invocationOperation {
// 处理事务
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self
selector:@selector(handleInvocation:) object:@"a"];
// 建立队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 操做加入队列
[queue addOperation:op];
}
复制代码
② 直接处理事务,不添加隐性队列函数式编程
- (void)invocationOperation {
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(handleInvocation:) object:@"a"];
[op start];
}
复制代码
③ 错误使用函数
- (void)invocationOperation {
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(handleInvocation:) object:@"a"];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:op];
[op start];
}
--------------------错误日志:-------------------
something is trying to start the receiver simultaneously from more than one thread'
--------------------错误日志:-------------------
复制代码
上述代码之因此会崩溃,是由于线程生命周期:post
[queue addOperation:op]
已经将处理事务的操做任务加入到队列中,并让线程运行 [op start]
将已经运行的线程再次运行会形成线程混乱
NSInvocationOperation和NSBlockOperation二者的区别在于:
前者相似target形式; 后者相似block形式——函数式编程,业务逻辑代码可读性更高
- (void)blockOperation {
// 初始化添加事务
NSBlockOperation *bo = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"任务1————%@",[NSThread currentThread]);
}];
// 添加事务1
[bo addExecutionBlock:^{
NSLog(@"任务2————%@",[NSThread currentThread]);
}];
// 添加事务2
[bo addExecutionBlock:^{
NSLog(@"任务3————%@",[NSThread currentThread]);
}];
// 添加事务3
[bo addExecutionBlock:^{
NSLog(@"任务4————%@",[NSThread currentThread]);
}];
// 添加事务4
[bo addExecutionBlock:^{
NSLog(@"任务5————%@",[NSThread currentThread]);
}];
// 回调监听
bo.completionBlock = ^{
NSLog(@"completionBlock执行,任务所有完成");
};
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:bo];
NSLog(@"事务添加进了NSOperationQueue");
}
--------------------输出结果:-------------------
事务添加进了NSOperationQueue
任务1————<NSThread: 0x280be7d00>{number = 3, name = (null)}
任务4————<NSThread: 0x280be7d00>{number = 3, name = (null)}
任务3————<NSThread: 0x280bc0340>{number = 6, name = (null)}
任务2————<NSThread: 0x280bd8600>{number = 5, name = (null)}
任务5————<NSThread: 0x280be7d00>{number = 3, name = (null)}
completionBlock执行,任务所有完成
--------------------输出结果:-------------------
复制代码
NSOperationQueue是异步执行的,因此任务12345的完成顺序不肯定,可是completionBlock回调会在任务所有完成后执行
还能够这样用
- (void)blockOperation {
// 初始化添加事务
NSBlockOperation *bo1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"任务1————%@",[NSThread currentThread]);
}];
NSBlockOperation *bo2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"任务2————%@",[NSThread currentThread]);
}];
NSBlockOperation *bo3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"任务3————%@",[NSThread currentThread]);
}];
NSBlockOperation *bo4 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"任务4————%@",[NSThread currentThread]);
}];
// bo1的回调监听
bo1.completionBlock = ^{
NSLog(@"bo1 的completionBlock执行,任务完成");
};
// bo2的回调监听
bo2.completionBlock = ^{
NSLog(@"bo2 的completionBlock执行,任务完成");
};
// bo3的回调监听
bo3.completionBlock = ^{
NSLog(@"bo3 的completionBlock执行,任务完成");
};
// bo4的回调监听
bo4.completionBlock = ^{
NSLog(@"bo4 的completionBlock执行,任务完成");
};
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:bo1];
[queue addOperation:bo2];
[queue addOperation:bo3];
[queue addOperation:bo4];
NSLog(@"事务添加进了NSOperationQueue");
}
--------------------输出结果:-------------------
事务添加进了NSOperationQueue
任务1————<NSThread: 0x2834dd200>{number = 7, name = (null)}
任务4————<NSThread: 0x2834d62c0>{number = 4, name = (null)}
任务2————<NSThread: 0x2834dd7c0>{number = 6, name = (null)}
任务3————<NSThread: 0x2834d6fc0>{number = 5, name = (null)}
bo4 的completionBlock执行,任务完成
bo1 的completionBlock执行,任务完成
bo3 的completionBlock执行,任务完成
bo2 的completionBlock执行,任务完成
--------------------输出结果:-------------------
复制代码
- (void)blockOperation {
NSBlockOperation *bo1 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 5; i++) {
//sleep(1);
NSLog(@"第一个操做 %d --- %@", i, [NSThread currentThread]);
}
}];
// 设置最高优先级
bo1.qualityOfService = NSQualityOfServiceUserInteractive;
NSBlockOperation *bo2 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 5; i++) {
NSLog(@"第二个操做 %d --- %@", i, [NSThread currentThread]);
}
}];
// 设置最低优先级
bo2.qualityOfService = NSQualityOfServiceBackground;
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:bo1];
[queue addOperation:bo2];
}
复制代码
NSOperation设置优先级只会让CPU有更高的概率调用,不是说设置高就必定所有先完成
在GCD中使用异步进行网络请求,而后回到主线程刷新UI.NSOperation中也有相似在线程间通信的操做
- (void)operationQueue {
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.name = @"Felix";
[queue addOperationWithBlock:^{
NSLog(@"请求网络%@--%@", [NSOperationQueue currentQueue], [NSThread currentThread]);
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSLog(@"刷新UI%@--%@", [NSOperationQueue currentQueue], [NSThread currentThread]);
}];
}];
}
复制代码
在GCD中只能使用信号量来设置并发数,而NSOperation能够直接经过设置maxConcurrentOperationCount
来设置并发数
- (void)operationQueue {
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.name = @"a";
queue.maxConcurrentOperationCount = 2;
for (int i = 0; i < 5; i++) {
[queue addOperationWithBlock:^{ // 一个任务
[NSThread sleepForTimeInterval:2];
NSLog(@"%d-%@",i,[NSThread currentThread]);
}];
}
}
复制代码
在NSOperation中经过addDependency
添加依赖能很好的控制任务执行的前后顺序
- (void)operationQueue {
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *bo1 = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:0.5];
NSLog(@"请求token");
}];
NSBlockOperation *bo2 = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:0.5];
NSLog(@"拿着token,请求数据1");
}];
NSBlockOperation *bo3 = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:0.5];
NSLog(@"拿着数据1,请求数据2");
}];
[bo2 addDependency:bo1];
[bo3 addDependency:bo2];
[self.queue addOperations:@[bo1,bo2,bo3] waitUntilFinished:YES];
NSLog(@"所有执行完毕");
}
--------------------输出结果:-------------------
请求token
拿着token,请求数据1
拿着数据1,请求数据2
所有执行完毕
--------------------输出结果:-------------------
复制代码
// 挂起
queue.suspended = YES;
// 继续
queue.suspended = NO;
// 取消
[queue cancelAllOperations];
复制代码
根据SDWebImage
加载网络图片的缓存机制,用NSOperation自定义图片缓存(本地缓存+内存缓存)
-(void)simulationCacheImage{
UIImage *cacheImage = self.imageCacheDict[model.imageUrl];
if (cacheImage) {
NSLog(@"从内存获取图片:%@", model.title);
cell.imageView.image = cacheImage;
return cell;
}
UIImage *diskImage = [UIImage imageWithContentsOfFile:[model.imageUrl getDowloadImagePath]];
if (diskImage) {
NSLog(@"从沙盒获取image:%@",model.title);
cell.imageView.image = diskImage;
[self.imageCacheDict setValue:diskImage forKey:model.imageUrl];
return cell;
}
NSBlockOperation *bo = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"去下载图片:%@", model.title);
// 延迟
NSData *data = [NSData dataWithContentsOfURL:imageURL];
UIImage *image = [UIImage imageWithData:data];
// 存内存
[self.imageCacheDict setValue:image forKey:model.imageUrl];
[data writeToFile:[model.imageUrl getDowloadImagePath] atomically:YES];
// 更新UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
cell.imageView.image = image;
}];
}];
[self.queue addOperation:bo];
}
复制代码