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设置一个分类,提供最便捷的图片处理方法!