我们可以使用NSOperation和NSOperationQueue实现多线程开发,NSOperationQueue的概念类似线程池,相比较NSThread,NSOperation提供了面向对象的语法。

使用NSOperation的过程是创建一个operation

NSOperation通常不会直接拿来使用,我们使用它的子类NSInvocationOperation或者NSBlockOperation,又或者自定义NSOperation的子类,下面分别介绍这三种方式。

NSInvocationOperation

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(threadOperation) object:nil];
    //如果直接使用start,operation会马上运行在主线程,我们基本不会这样使用
    //[operation start];
    
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    //将operation加入到operationqueue后,operationqueue会自动执行start方法,且运行在多线程中
    [queue addOperation:operation];
}

 

关于NSOperationQueue我们还可以设置它的相关属性

queue.maxConcurrentOperationCount  获取或者设置最大的并发运行数,默认值为-1,表示不限制同时运行的线程数

queue.name  设置线程池名称

[NSOperationQueue currentQueue]  获取当前线程池

[NSOperationQueue mainQueue]  获取主线程池

[queue cancelAllOperations]  对线程池中的线程执行cancel方法设置取消标记,

 

NSBlockOperation

NSBlockOpration的使用流程基本与NSInvocationOperation一致,只是使用了block代替selector

 

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    
    _imageview = [[UIImageView alloc] initWithFrame:CGRectMake(40, 40, 100, 80)];
    [self.view addSubview:_imageview];
    
    
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        
        NSLog(@"%hhd",[NSThread isMainThread]);
        NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://www.iyi8.com/uploadfile/2014/1002/20141002112243630.jpg"]];
        
        
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            NSLog(@"%hhd",[NSThread isMainThread]);
            [_imageview setImage:[UIImage imageWithData:data]];
        }];
    }];
    
    //为operation添加执行完毕的block
    operation.completionBlock = ^{
        NSLog(@"operation done");
    };
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperation:operation];
}

 

上面代码在多线程中网络请求图片,并在主线程中对ui进行更新,NSBlockOperation比NSInvocationOperation更加方便常用

 

自定义NSOperation子类

上面我们直接使用了框架中提供的NSOperation子类,我们还可以自己定义NSoperation子类

自定义Operation的步骤如下

1 继承NSOperation

2 实现main函数

3 在main函数中创建自动释放池

4 把任务代码写在自动释放池中

下面我们自定义一个NSOperation子类,用来下载图片

ZLTOperation.h
#import <Foundation/Foundation.h>

@protocol DownImageProtocal;

@interface ZLTOperation : NSOperation
-(id)initWithImageUrl:(NSURL *)url delagete:(id<DownImageProtocal>) delegate;
@end

@protocol DownImageProtocal <NSObject>

-(void)updateViewWithImage:(UIImage *)image;

@end

ZLTOperation.m
#import "ZLTOperation.h"
@interface ZLTOperation() {
    NSURL *_imageUrl;
    id<DownImageProtocal> _delegate;
}
@end

@implementation ZLTOperation
-(id)initWithImageUrl:(NSURL *)url delagete:(id<DownImageProtocal>) delegate {
    self = [super init];
    if (self) {
        _imageUrl = url;
        _delegate = delegate;
    }
    return self;
}

- (void)main {
    @autoreleasepool {
        //任务是否被取消
        if ([self isCancelled]) {
            return;
        }
        
        NSData *data = [NSData dataWithContentsOfURL:_imageUrl];
        
        //任务是否被取消
        if ([self isCancelled]) {
            return;
        }
        
        if ([_delegate respondsToSelector:@selector(updateViewWithImage:)]) {
            [(NSObject *)_delegate performSelectorOnMainThread:@selector(updateViewWithImage:) withObject:[UIImage imageWithData:data] waitUntilDone:NO];
        }
    }
}
@end

 

测试

ZViewController.h
@interface ZViewController : UIViewController<DownImageProtocal>

@end

ZViewController.m
@interface ZViewController () {
    UIImageView *_imageview;
}

@end

@implementation ZViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    
    _imageview = [[UIImageView alloc] initWithFrame:CGRectMake(40, 40, 100, 80)];
    [self.view addSubview:_imageview];
    
    ZLTOperation *operation = [[ZLTOperation alloc] initWithImageUrl:[NSURL URLWithString:@"http://www.iyi8.com/uploadfile/2014/1002/20141002112243630.jpg"] delagete:self];
    
    
    //为operation添加执行完毕的block
    operation.completionBlock = ^{
        NSLog(@"operation done");
    };
    
    
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperation:operation];
}

- (void)updateViewWithImage:(UIImage *)image {
    _imageview.image = image;
}
@end

 

NSOperation其他常用方法和属性

- (BOOL)isCancelled;

- (void)cancel;  取消任务

  

- (void)setCompletionBlock:(void (^)(void))block  添加任务完成block

 

- (double)threadPriority

- (void)setThreadPriority:(double)p   任务优先级

 

- (void)addDependency:(NSOperation *)op;

- (void)removeDependency:(NSOperation *)op;

- (NSArray *)dependencies;  添加依赖

依赖用来控制线程执行顺序,如果A添加B为他的依赖,那么只有当操作B完成后才会执行操作A,操作依赖关系可以设置多个,例如A依赖于B、B依赖于C…但是千万不要设置为循环依赖关系(例如A依赖于B,B依赖于C,C又依赖于A),否则是不会被执行的。

 

并发的NSOperation

前面提过直接执行operation的start方法时,operation会运行在主线程,这是因为NSOperation默认是非并发的,我们也可以自己实现并发的operation类,这需要重载一系列方法,创建并发operation的一般步骤如下

  1. 你需要创建一个subclass
  2. 除了重载main方法,实现并发你还需要至少重载start,isConcurrent,isExecuting,isFinished四个方法
  3. start里,创建Thread或者调用一个异步函数
  4. 更新isExecuting,并且发送相应KVO消息
  5. 任务结束后,你还得更新isExecuting 和 isFinished,发送相应KVO消息

一般我们不需要创建并发的NSOperation,配合NSOperationQueue来调用,而不是自己调用start

posted on 2014-11-09 16:17  幸福小弥  阅读(310)  评论(0编辑  收藏  举报