您的位置:首页 > 运维架构

自定义NSOperation

2015-12-27 02:59 411 查看
通过上篇文章的学习我们知道, NSOperation系统自带的方法无论是取消还是挂起操作只能控制队列中的操作, 都无法操纵正在线程中运行的操作; 下面我们通过自定义NSOperation

准备工作

自定义 DownloadImageOperation 继承自 NSOperation

代码调用

// 实例化自定义操作
DownloadImageOperation *op = [[DownloadImageOperation alloc] init];
// 将自定义操作添加到下载队列
[self.downloadQueue addOperation:op];


需求驱动开发

目标一: 设置自定义操作的执行入口

对于自定义操作,只要重写了 main 方法,当队列调度操作执行时,会自动运行 main 方法

- (void)main {
@autoreleasepool {
NSLog(@"%@", [NSThread currentThread]);
}
}


目标二:给自定义参数传递参数

定义属性

/// 要下载图像的 URL 字符串
@property (nonatomic, copy) NSString *URLString;


代码调用

// 实例化自定义操作
DownloadImageOperation *op = [[DownloadImageOperation alloc] init];
// 设置操作属性
op.URLString = @"https://www.baidu.com/img/bdlogo.png";

// 将自定义操作添加到下载队列,操作启动后会执行 main 方法
[self.downloadQueue addOperation:op];


注意: 只有将操作添加到队列中操作才会执行, 也就是说才会执行main函数; 这时参数也就传递到操作中啦;

目标3: 如何回调?

利用系统提供的 CompletionBlock 属性

// 设置完成回调
[op setCompletionBlock:^{
NSLog(@"完成 %@", [NSThread currentThread]);
}];


1> 只要设置了 CompletionBlock,当操作执行完毕后,就会被自动调用

2> CompletionBlock 既不在主线程也不在操作执行所在线程

3> CompletionBlock 无法传递参数

自己定义回调 Block,在操作结束后执行

1> 定义属性

/// 完成回调 Block
@property (nonatomic, copy) void (^finishedBlock)(UIImage *image);


2> 设置自定义回调

// 设置自定义完成回调
[op setFinishedBlock:^(UIImage *image) {
NSLog(@"finished %@ %@", [NSThread currentThread], image);
}];


3> 耗时操作后执行回调

// 判断自定义回调是否存在
if (self.finishedBlock != nil) {
// 通常为了简化调用方的代码,异步操作结束后的回调,大多在主线程
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.finishedBlock(@"hello");
}];
}


目标4: 简化操作创建

定义类方法

+ (instancetype)downloadImageOperationWithURLString:(NSString *)URLString finished:(void (^)(UIImage *image))finished;


实现类方法

+ (instancetype)downloadImageOperationWithURLString:(NSString *)URLString finished:(void (^)(UIImage *))finished {
DownloadImageOperation *op = [[DownloadImageOperation alloc] init];

op.URLString = URLString;
op.finishedBlock = finished;

return op;
}


方法调用

// 使用类方法实例化下载操作
DownloadImageOperation *op = [DownloadImageOperation downloadImageOperationWithURLString:@"http://www.baidu.com/img/bdlogo.png" finished:^(UIImage *image) {
NSLog(@"%@", image);
}];

// 将自定义操作添加到下载队列,操作启动后会执行 main 方法
[self.downloadQueue addOperation:op];


取消操作

在关键节点进行判断isCancelled

添加多个下载操作

for (int i = 0; i < 10; ++i) {
NSString *urlString = [NSString stringWithFormat:@"http://www.xxx.com/%04d.png", i];

DownloadImageOperation *op = [DownloadImageOperation downloadImageOperationWithURLString:urlString finished:^(UIImage *image) {
NSLog(@"===> %@", image);
}];

// 将自定义操作添加到下载队列,操作启动后会执行 main 方法
[self.downloadQueue addOperation:op];
}


内存警告时取消所有操作

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];

[self.downloadQueue cancelAllOperations];
}


cancelAllOperations 会向队列中的所有操作发送 Cancel 消息

调整 main 方法,在关键节点判断

- (void)main {
NSLog(@"%s", __FUNCTION__);

@autoreleasepool {

NSLog(@"下载图像 %@", self.URLString);
// 模拟延时
[NSThread sleepForTimeInterval:1.0];

if (self.isCancelled) {
NSLog(@"1.--- 返回");
return;
}

// 判断自定义回调是否存在
if (self.finishedBlock != nil) {
// 通常为了简化调用方的代码,异步操作结束后的回调,大多在主线程
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.finishedBlock(self.URLString);
}];
}
}
}


知识补充:

NSOperation的start与main函数

队列调度操作时,首先执行 start 方法将线程放入可调度线程池

操作执行时的入口是 main 方法

start方法默认会判断线程状态, 如果状态已经完成或取消,就不会调用main方法

如果操作没有执行, 接收到cancel消息, start方法同样会被调用
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: