自定义NSOperation

如果NSInvocationOperation和NSBlockOperation对象不能满足需求, 我们可以直接继承NSOperation, 并添加额外的功能。

继承所需的工作量主要取决于你要实现非并发还是并发的NSOperation。定义同步的NSOperation要简单许多,只需要重载-main这个方法,在这个方法里面执行主任务,并正确地响应取消事件; 对于异步NSOperation, 必须重写NSOperation的多个基本方法进行实现(main、start)。

1. 自定义非并发NSOperation

  1. 重写NSOperation的main方法,该方法会作为NSOperation所启动线程的执行体
    • 在 operation 的 main 方法里面,必须提供 autorelease pool,因为你的 operation 完成后需要销毁。
  2. 实现KVO机制,一旦你的 operation 开始了,必须通过 KVO,告诉所有的监听者,现在该operation的执行状态。(即修改isFinished,isExecuting的值)
  3. 重载 isExecuting方法和isFinished方法。在这两个方法中,必须返回一个线程安全值(通常是个BOOL值),这个值可以在 operation 中进行操作。

1.1 示例代码:

.h文件:

typedef void(^HYBDownloadReponse)(UIImage *image);

@interface DownloadOperation : NSOperation

@property (nonatomic, copy) NSString *url;
@property (nonatomic, copy) HYBDownloadReponse responseBlock;

- (instancetype)initWithUrl:(NSString *)url completion:(HYBDownloadReponse)completion;

@end	

.m文件:

#import "DownloadOperation.h"

@interface DownloadOperation () {
BOOL _isFinished;
BOOL _isExecuting;
}

@end

@implementation DownloadOperation

- (instancetype)initWithUrl:(NSString *)url completion:(HYBDownloadReponse)completion {
if (self = [super init]) {
    self.url = url;
    self.responseBlock = completion;
}

return self;
}

// 必须重写这个主方法
- (void)main {
// 新建一个自动释放池,如果是异步执行操作,那么将无法访问到主线程的自动释放池
@autoreleasepool {
    // 提供一个变量标识,来表示我们需要执行的操作是否完成了,当然,没开始执行之前,为NO
    BOOL taskIsFinished = NO;
    UIImage *image = nil;
    
    // while 保证:只有当没有执行完成和没有被取消,才执行我们自定义的相应操作
    while (taskIsFinished == NO && [self isCancelled] == NO){
        // 获取图片数据
        NSURL *url = [NSURL URLWithString:self.url];
        NSData *imageData = [NSData dataWithContentsOfURL:url];
        image = [UIImage imageWithData:imageData];
        NSLog(@"Current Thread = %@", [NSThread currentThread]);
        
        // 这里,设置我们相应的操作都已经完成,后面就是要通知KVO我们的操作完成了。
        
        taskIsFinished = YES;
    }
    
    // KVO 生成通知,告诉其他线程,该operation 执行完了
    [self willChangeValueForKey:@"isFinished"];
    [self willChangeValueForKey:@"isExecuting"];
    _isFinished = YES;
    _isExecuting = NO;
    [self didChangeValueForKey:@"isFinished"];
    [self didChangeValueForKey:@"isExecuting"];
    
    if (self.responseBlock) {
        dispatch_async(dispatch_get_main_queue(), ^{
            self.responseBlock(image);
        });
    }
}
}

- (void)start {
// 如果我们取消了在开始之前,我们就立即返回并生成所需的KVO通知
if ([self isCancelled]){
    // 我们取消了该 operation,那么就要告诉KVO,该operation已经执行完成(isFinished)
    // 这样,调用的队列(或者线程)会继续执行。
    [self willChangeValueForKey:@"isFinished"];
    _isFinished = NO;
    [self didChangeValueForKey:@"isFinished"];
} else {
    // 没有取消,那就要告诉KVO,该队列开始执行了(isExecuting)!那么,就会调用main方法,进行同步执行。
    [self willChangeValueForKey:@"isExecuting"];
    _isExecuting = YES;
    [NSThread detachNewThreadSelector:@selector(main) toTarget:self withObject:nil];
    [self didChangeValueForKey:@"isExecuting"];
}
}

- (BOOL)isExecuting {
return _isExecuting;
}

- (BOOL)isFinished {
return _isFinished;
}

@end	

1.2调用

queue方式:

DownloadOperation *operation = [[DownloadOperation alloc] initWithUrl:@"https://mmbiz.qlogo.cn/mmbiz/sia5QxFVcFD0wkCgnmf6DVxI6fVewNS8rhtZb71v2DMpDy8jIdtviaetzicwQzTEoKKyHAN96Beibk2G61tZpezQ0Q/0?wx_fmt=png" completion:^(UIImage *image) {
//    self.imageView.image = image;
}];

DownloadOperation *operation1 = [[DownloadOperation alloc] initWithUrl:@"https://mmbiz.qlogo.cn/mmbiz/sia5QxFVcFD0wkCgnmf6DVxI6fVewNS8rhtZb71v2DMpDy8jIdtviaetzicwQzTEoKKyHAN96Beibk2G61tZpezQ0Q/0?wx_fmt=png" completion:^(UIImage *image) {
//    self.imageView1.image = image;
}];

NSOperationQueue *queue = [[NSOperationQueue alloc]init];
[queue addOperation:operation];
[queue addOperation:operation1];
[queue setMaxConcurrentOperationCount:2];

手动方式:

DownloadOperation *operation = [[DownloadOperation alloc] initWithUrl:@"https://mmbiz.qlogo.cn/mmbiz/sia5QxFVcFD0wkCgnmf6DVxI6fVewNS8rhtZb71v2DMpDy8jIdtviaetzicwQzTEoKKyHAN96Beibk2G61tZpezQ0Q/0?wx_fmt=png" completion:^(UIImage *image) {
//    self.imageView.image = image;
}];
[operation start];		

2. 自定义并发NSOperation

  1. 和同步一样需要 重写mian start
  2. 和同步一样需要 使用KVO通知状态改变
  3. 和同步一样需要 重写isExecuting isFinishing
  4. 比同步多的是 重写isAsynchronous(或者isConcurrent)

2.1 示例代码:

#import "DownloadOperation.h"

@interface DownloadOperation () {
@private BOOL _isFinished;
@private BOOL _isExecuting;
}

@end

@implementation DownloadOperation

- (instancetype)initWithUrl:(NSString *)url completion:(HYBDownloadReponse)completion {
if (self = [super init]) {
    self.url = url;
    self.responseBlock = completion;
}

return self;
}

// 必须重写这个主方法
- (void)main {
// 新建一个自动释放池,如果是异步执行操作,那么将无法访问到主线程的自动释放池
@autoreleasepool {
    UIImage *image = nil;
    if (!self.isCancelled) {
        // 获取图片数据
        NSURL *url = [NSURL URLWithString:self.url];
        NSData *imageData = [NSData dataWithContentsOfURL:url];
        image = [UIImage imageWithData:imageData];
    }
    
    NSLog(@"currentThread: %@", [NSThread currentThread]);
    
    // 被取消,也可能发生在转换的地方
    if (self.isCancelled) {
        image = nil;
    }
    
    
    if (![self isCancelled] && self.responseBlock) {
        dispatch_async(dispatch_get_main_queue(), ^{
            self.responseBlock(image);
        });
    }
}
}

// 与自定义同步NSOperation不同的是,必须要实现下面的方法
#if __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_7_0
- (BOOL)isConcurrent {
return YES;
}

#else

- (BOOL)isAsynchronous {
return YES;
}
#endif

@end
posted @ 2016-04-19 11:18  孙焱焱  阅读(850)  评论(0编辑  收藏  举报