自定义NSOperation
如果NSInvocationOperation和NSBlockOperation对象不能满足需求, 我们可以直接继承NSOperation, 并添加额外的功能。
继承所需的工作量主要取决于你要实现非并发
还是并发
的NSOperation。定义同步的NSOperation要简单许多,只需要重载-main这个方法,在这个方法里面执行主任务,并正确地响应取消事件; 对于异步NSOperation, 必须重写NSOperation的多个基本方法进行实现(main、start)。
1. 自定义非并发NSOperation
- 重写NSOperation的
main
方法,该方法会作为NSOperation所启动线程的执行体- 在 operation 的 main 方法里面,必须提供 autorelease pool,因为你的 operation 完成后需要销毁。
- 实现KVO机制,一旦你的 operation 开始了,必须通过 KVO,告诉所有的监听者,现在该operation的执行状态。(即修改isFinished,isExecuting的值)
- 重载 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
- 和同步一样需要 重写mian start
- 和同步一样需要 使用KVO通知状态改变
- 和同步一样需要 重写isExecuting isFinishing
- 比同步多的是 重写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