NSOperation和NSOperationQueue多线程的使用

最近学习了多线程编程 NSOperationQueue ,才比较清楚的明白了同步/异步阻塞/非阻塞所表达的不同含义:所谓的同步/异步是对你得到消息的方式的描述,而阻塞/非阻塞则是你怎么样处理事情的做法,他们讲述的是不同层面的概念。

现在来讲述一下可以实现多线程的三种方式中的 NSOperation 类。使用NSOperation和NSOperationQueue实现多线程编程,实现步骤大致是这样的:

1> 先将需要执行的操作封装到一个NSOperation对象中

2> 然后将NSOperation对象添加到NSOperationQueue中

3> 系统会自动将NSOperation中封装的操作放到一条新线程中执行

而由于 NSOperation 是一个抽象类,所以它的功能只能有其子类 NSBlockOperation 和 SInvocationOperation 来实现。

NSBlockOperation 的使用

 NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^(){     
    NSLog(@"*执行第1次操作,线程:%@", [NSThread currentThread]);       
   }];

[operation addExecutionBlock:^() {

   NSLog(@"**又执行了1个新的操作,线程:%@", [NSThread currentThread]);

   }];

// 开始执行任务    
[operation start];

注:默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作。只有将operation放到一个NSOperationQueue中,才会异步执行操作。

NSInvocationOperation 的使用

ration2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downLoadImage:) object:URL];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:operation2];

如果是有刷新界面的操作,那么必须将这个操作交给主线程来完成。

自定义 NSOperation 类

由于 NSOperation 类功能比较少, 一般情况下我们可以自定义一个 NSOperation 类。要定制这样的一个操作,可以遵循以下步骤:

  1. 继承 NSOperation 类
  2. 重写“main”方法
  3. 在“main”方法中创建一个“automaticreleasepool“
  4. 将你的代码放在”autoreleasepool“中

例子:
* .h 文件

#import <Foundation/Foundation.h>
@interface MyLengthyOperation : NSOperation
@property (nonatomic, strong, readonly) NSString *mark;
- (instancetype)initWithMark:(NSString *)mark;
@end 

* .m 文件

#import "MyLengthyOperation.h"
@interface MyLengthyOperation ()
@property (nonatomic, strong, readwrite) NSString *mark;
@end
@implementation MyLengthyOperation
- (instancetype)initWithMark:(NSString *)mark {  
   self = [super init];
   if (self) {      
       self.mark = mark;
    }
   return self;
   }
- (void)main {
    @autoreleasepool {
      for (int i = 0; i < 100; i ++) {
         if (self.isCancelled) {
             break;
         }
         NSLog(@"%@ - %f", self.mark, sqrt(i));
        }
    }
}

@end

基本操作

//  初始化任务 
MyLengthyOperation *operation_1 = [[MyLengthyOperation alloc] initWithMark:@"operation_1"];
MyLengthyOperation *operation_2 = [[MyLengthyOperation alloc] initWithMark:@"operation_2"];

// 设置任务的优先级
[operation_1 setQueuePriority:NSOperationQueuePriorityVeryLow];
[operation_2 setQueuePriority:NSOperationQueuePriorityHigh];

// 添加从属关系
[operation_2 addDependency:operation_1]; (任务2在任务1结束后才会执行)

// 初始化队列
NSOperationQueue *myQueue = [[NSOperationQueue alloc] init];
myQueue.name = @"下载队列";

// 将一个简单的任务队列添加进队列中 (使用 Block 回调方式)      
    NSURL *aURL = [NSURL URLWithString:URL];
    NSData *data = [NSData dataWithContentsOfURL:aURL];

    [[NSOperationQueue mainQueue] addOperationWithBlock:^{

        UIImage *image = [[UIImage alloc] initWithData:data];
        [self performSelectorOnMainThread:@selector(updateUI:) withObject:image waitUntilDone:YES];
    }];
}];

// 设置任务队列的最大并发数
 myQueue.maxConcurrentOperationCount = 4;

// 添加进任务队列中执行
[myQueue addOperation:operation_1];
[myQueue addOperation:operation_2];

// 取消所有操作
[myQueue cancelAllOperations];

// 执行延时的操作(将执行代码放置在 block 中)
- (void)execute:(dispatch_block_t)block afterDelay:(int64_t)delta {

   dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delta), dispatch_get_main_queue(), block);
 }

// 挂起操作
[myQueue setSuspended:YES];

// 取消任务
[operation_1 cancel];

//  任务完成后的回调可以表示任务已经结束
 [operation_1 setCompletionBlock:^{
      NSLog(@"任务1结束");
  }];
 [operation_2 setCompletionBlock:^{
      NSLog(@"任务2结束");
 }];