NSoperation作为一个抽象类,本身不具备封装操作的能力,所以必须使用它的子类来实现多线程操作,但当子类也无法达到我们的需求时,我们可以自定义一个NSoperation的子类,自己写实现方法!
自定义NSoperation的步骤很简单,只要重写main方法,在里面实现想执行的异步任务,但如果是异步操作,也就无法访问到主线程的自动释放池,所以需要自己手动创建自动释放池!
现在我们来实现一个需求,自定义一个NSOperation子类,实现网络图片下载,再在主线程上设置图片!
首先需要一个继承自NSOperation的子类,该类提供一个可以实例化操作的方法
#import <UIKit/UIKit.h>
@interface CXWebImageOperation : NSOperation
/**
* 实例化web图片操作
*/
+ (instancetype)webImageOperationWithUrl:(NSString *)urlString completion:(void(^) (UIImage *image))completion;
@end
其中urlString是提供给控制器设置图片链接的参数,而completion是一个提供给控制器编写执行完下载操作后要执行的代码,image是操作传回来的已下载完的图片对象
再来看一下该类的实现文件,.m文件里面提供了两个私有变量
@interface CXWebImageOperation ()
/**
* 图片的url
*/
@property (nonatomic,copy) NSString *urlString;
/**
* 下载完图片后需要执行的代码块
*/
@property (nonatomic,copy) void (^completion)(UIImage *image);
@end
image自定义操作最主要的目的是重写main方法,而main方法里面想要访问到图片链接以及代码块只能通过成员变量!该类提供的类方法实现如下:
+ (instancetype)webImageOperationWithUrl:(NSString *)urlString completion:(void(^) (UIImage *image))completion
{
CXWebImageOperation *op = [[self alloc] init];
op.urlString = urlString;
op.completion = completion;
return op;
}
为成员变量赋值。
重写main方法
- (void)main
{
@autoreleasepool {
// 下载图片的耗时操作
NSURL *url = [NSURL URLWithString:self.urlString];
NSData *data = [NSData dataWithContentsOfURL:url];
NSLog(@"已下载 %@",[NSThread currentThread]);
UIImage *image = [UIImage imageWithData:data];
// 主线程回调,完成操作后通知调用方完成回调
dispatch_async(dispatch_get_main_queue(), ^{
if (self.completion != nil) {
self.completion(image);
}
});
}
}
在控制器里面实现对图片的设置,在这之前,我们需要在storyboard里面添加一个UIImageView控件并拖线进控制器里面
@interface ViewController ()
/**
* 图片
*/
@property (weak, nonatomic) IBOutlet UIImageView *iconV;
/**
* 操作队列
*/
@property (nonatomic,strong) NSOperationQueue *queue;
@end
用懒加载的方式初始化队列
- (NSOperationQueue *)queue
{
if (_queue == nil) {
_queue = [[NSOperationQueue alloc] init];
}
return _queue;
}
初始化操作并执行
- (void)viewDidLoad {
[super viewDidLoad];
// 图片下载操作
CXWebImageOperation *op = [CXWebImageOperation webImageOperationWithUrl:@"http://p17.qhimg.com/dr/48_48_/t012d281e8ec8e27c06.png" completion:^(UIImage *image) {
NSLog(@"%@",[NSThread currentThread]);
// block回调在主线程更新UI
self.iconV.image = image;
}];
// 添加进队列
[self.queue addOperation:op];
}
此时自定义操作已基本完成!
控制台打印结果如下:
可以在虚拟机上看到效果如下
该图片已从网络下载并展现在屏幕上!
当然以上只是一个简单地自定义操作的演示,且不说调用比较麻烦,光是从队列操作上图片缓存上就没有做任何优化处理!要实现一个完整的网络图片下载操作,还需要一个单例类来进行缓存及操作的优化管理,同时也需要为UIImageView设置一个分类,提供最便捷的图片处理方法!