iOS 多线程开发之OperationQueue(一)概念+两种Operation
2015-06-09 14:51
543 查看
原创blog,转载请注明出处
blog.csdn.net/hello_hwc
欢迎关注我的iOS SDK详解专栏,这里有很多基础的文章
http://blog.csdn.net/column/details/huangwenchen-ios-sdk.html
前言:在iOS开发 中,多线程是一个很重要的一个方面。iOS的多线程使用可以分为几个方面。由底层到上层分别是
pthread
NSThread
GCD
NSOperationQueue
这里不得不提到一点,操作系统是以线程为任务调度基本单位,对于一个单核CPU,实际上是采用时间片轮转来实现多线程的。而对于多核CPU,则可以实现真正的并发执行。
我的iOS SDK讲解系列并不会详细讲解pthread 和 NSThread,二者很少会用到,也很容易在使用时候出问题。
本文会简单讲解下NSOperationQueue以及NSOperation,并且给出一个简单的Demo实现串行和并行的NSOperation
NSThread是对pthread的进一步封装,但是仍然在使用的时候出现各种各样的问题。
在iOS开发的过程中,有一点要谨记:
尽可能的使用上层API去实现,除非上层API没办法实现再考虑底层API
使用NSOperationQueue和NSOperation相关的API可以方便而安全的的进行
取消一个任务
添加任务的依赖关系,例如任务3要再任务1和任务2执行完毕才能执行
设置一个Queue最多同时执行的任务数量。默认情况Queue会做到最优化执行。
KVO监听任务的状态变化
另外,NSOperationQueue也分为两种
mainQueue(主线程),注意只能在主线程处理UI相关。
自定义Queue(后台线程)。
当把一个NSOperation添加到自定义Queue后,会在当前线程之外的另一个线程来执行操作。
- 为下载提供独立的进度条
- 每次队列只允许一个下载任务进行
下载链接
NSOperation用来封装要实现的任务,注意这些任务是一次性的,不能重复利用。通常执行任务要把Operation添加到OperationQueue。等到任务执行完毕后,Operation会被从Queue上移除并且销毁。
有几点要提到:
NSOperation是一个抽象类,使用的时候要使用它的子类来定义任务
NSOperation是线程安全的,也就是说在不同线程上访问NSOperation对象是安全的。同样,为NSOperation子类添加的方法也要是线程安全的。
NSOperation分为两种,一种是同步的,一种是异步的。这个接下来会详细讲解
NSOperation的很多属性支持KVO
支持KVO的属性
注意:
init和start函数都是在创建NSOperation线程上执行的。
main是在后台线程上执行的。
对于同步的NSOperation来说,只需要重写main函数,保证main函数里的任务是同步执行的。
例如用NSOperationQueue下载一副图片。
头文件
我们传入imageview,task负责更新UI
.m文件
如何使用
这里我们异步的用NSURLSession去下载,并且实时返回下载的进度。
头文件
.m文件
blog.csdn.net/hello_hwc
欢迎关注我的iOS SDK详解专栏,这里有很多基础的文章
http://blog.csdn.net/column/details/huangwenchen-ios-sdk.html
前言:在iOS开发 中,多线程是一个很重要的一个方面。iOS的多线程使用可以分为几个方面。由底层到上层分别是
pthread
NSThread
GCD
NSOperationQueue
这里不得不提到一点,操作系统是以线程为任务调度基本单位,对于一个单核CPU,实际上是采用时间片轮转来实现多线程的。而对于多核CPU,则可以实现真正的并发执行。
我的iOS SDK讲解系列并不会详细讲解pthread 和 NSThread,二者很少会用到,也很容易在使用时候出问题。
本文会简单讲解下NSOperationQueue以及NSOperation,并且给出一个简单的Demo实现串行和并行的NSOperation
简单介绍下pthread和NSThread
pthread 是Unix层次的线程,相当底层。可以参见WIKI。NSThread是对pthread的进一步封装,但是仍然在使用的时候出现各种各样的问题。
在iOS开发的过程中,有一点要谨记:
尽可能的使用上层API去实现,除非上层API没办法实现再考虑底层API
NSOperationQueue概述
NSOperationQueue把线程操作抽象成队列操作,它是GCD的上层封装,队列中是一个个的NSOperation,NSOperation用来定义任务的具体执行。使用NSOperationQueue和NSOperation相关的API可以方便而安全的的进行
取消一个任务
添加任务的依赖关系,例如任务3要再任务1和任务2执行完毕才能执行
设置一个Queue最多同时执行的任务数量。默认情况Queue会做到最优化执行。
KVO监听任务的状态变化
另外,NSOperationQueue也分为两种
mainQueue(主线程),注意只能在主线程处理UI相关。
自定义Queue(后台线程)。
当把一个NSOperation添加到自定义Queue后,会在当前线程之外的另一个线程来执行操作。
NSOperation 概述
写了个简单的Demo,异步下载3张图片。- 为下载提供独立的进度条
- 每次队列只允许一个下载任务进行
下载链接
NSOperation用来封装要实现的任务,注意这些任务是一次性的,不能重复利用。通常执行任务要把Operation添加到OperationQueue。等到任务执行完毕后,Operation会被从Queue上移除并且销毁。
有几点要提到:
NSOperation是一个抽象类,使用的时候要使用它的子类来定义任务
NSOperation是线程安全的,也就是说在不同线程上访问NSOperation对象是安全的。同样,为NSOperation子类添加的方法也要是线程安全的。
NSOperation分为两种,一种是同步的,一种是异步的。这个接下来会详细讲解
NSOperation的很多属性支持KVO
支持KVO的属性
isCancelled - read-only isAsynchronous - read-only isExecuting - read-only isFinished - read-only isReady - read-only dependencies - read-only queuePriority - readable and writable completionBlock - readable and writable
同步的NSOperation
注意,这里的同步异步是相对任务本身的,同步的任务指的是start-main-结束,main结束后,则任务结束。异步的任务例如有网络请求的,main函数结束了,任务还没结束。注意:
init和start函数都是在创建NSOperation线程上执行的。
main是在后台线程上执行的。
对于同步的NSOperation来说,只需要重写main函数,保证main函数里的任务是同步执行的。
例如用NSOperationQueue下载一副图片。
头文件
我们传入imageview,task负责更新UI
#import <Foundation/Foundation.h> #import <UIKit/UIKit.h> @interface SycDownloadImageOperation : NSOperation -(instancetype)initWithURL:(NSURL *)url ImageView:(UIImageView *)imageview; @end
.m文件
#import "SycDownloadImageOperation.h" @interface SycDownloadImageOperation() @property (copy,nonatomic)NSURL * url; @property (weak,nonatomic)UIImageView * imageview; //不参对应的UIImageview生命周期 @end @implementation SycDownloadImageOperation -(instancetype)initWithURL:(NSURL *)url ImageView:(UIImageView *)imageview{ self = [super init]; if (!self) { return nil; } _url = url; _imageview = imageview; return self; } -(void)main{ if ([self isCancelled]) { return; } NSData * data = [NSData dataWithContentsOfURL:self.url options:NSDataReadingUncached error:nil]; if ([self isCancelled]) { return; } UIImage *image = [[UIImage alloc] initWithData:data]; //注意,这里一定要到主线程上更新UI dispatch_async(dispatch_get_main_queue(), ^{ _imageview.image = image; }); } @end
如何使用
SycDownloadImageOperation * operation = [[SycDownloadImageOperation alloc] initWithURL:imageURL ImageView:imageview]; [self.downloadQueue addOperation:operation];
异步的NSOperation
异步的NSOperation要自己维护队列的状态。就是isFinished和
isExecuting两个属性。至少要实现的四个方法
isFinished isExecuting isAsynchronous start
这里我们异步的用NSURLSession去下载,并且实时返回下载的进度。
头文件
#import <Foundation/Foundation.h> #import <UIKit/UIKit.h> typedef void(^ProgressBlock)(CGFloat currentProgress); typedef void(^FinishBlock)(BOOL successed,NSError * error,UIImage * image); @interface DownloadImageOperation : NSOperation -(instancetype)initWithURL:(NSURL*)url ProgressCallBack:(ProgressBlock)progressCallBack FinishedCallBack:(FinishBlock)finishedCallBack; @end
.m文件
#import "DownloadImageOperation.h" @interface DownloadImageOperation ()<NSURLSessionDelegate,NSURLSessionDownloadDelegate>{ BOOL finished; BOOL executing; } @property (strong,nonatomic)NSURL * url; @property (strong,nonatomic)NSURLSession * session; @property (copy,nonatomic)ProgressBlock progressBlock; @property (copy,nonatomic)FinishBlock finishBlock; @property (strong,nonatomic)UIImage * image; @property (strong,nonatomic)NSURL * cacheURL; @end @implementation DownloadImageOperation -(instancetype)initWithURL:(NSURL*)url CacheURL:(NSURL *)cacheurl ProgressCallBack:(ProgressBlock)progressCallBack FinishedCallBack:(FinishBlock)finishedCallBack{ self = [super init]; if (!self) { return nil; } _url = url; _progressBlock = progressCallBack; _finishBlock = finishedCallBack; _cacheURL = cacheurl; finished = NO; executing = NO; return self; } -(instancetype)initWithURL:(NSURL *)url ProgressCallBack:(ProgressBlock)progressCallBack FinishedCallBack:(FinishBlock)finishedCallBack{ return [self initWithURL:url CacheURL:nil ProgressCallBack:progressCallBack FinishedCallBack:finishedCallBack]; } -(void)start{ if ([self isCancelled]) { // Must move the operation to the finished state if it is canceled. [self willChangeValueForKey:@"isFinished"]; finished = NO; [self didChangeValueForKey:@"isFinished"]; return; } // If the operation is not canceled, begin executing the task. [self willChangeValueForKey:@"isExecuting"]; [NSThread detachNewThreadSelector:@selector(main) toTarget:self withObject:nil]; executing = YES; [self didChangeValueForKey:@"isExecuting"]; } -(BOOL)isFinished{ return finished; } -(BOOL)isExecuting{ return executing; } -(BOOL)isAsynchronous{ return YES; } -(void)main{ self.session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue currentQueue]]; NSURLSessionDownloadTask * task = [self.session downloadTaskWithURL:self.url]; [task resume]; } -(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{ BOOL successed = self.image == nil?NO:YES; dispatch_async(dispatch_get_main_queue(), ^(void) { self.finishBlock(successed,error,self.image); }); [self setOperationFinished]; return; } -(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{ NSData * data = [NSData dataWithContentsOfURL:location]; self.image = [UIImage imageWithData:data]; if (self.cacheURL != nil) { NSFileManager * defaultManager = [NSFileManager defaultManager]; NSError * error = nil; [defaultManager moveItemAtURL:location toURL:self.cacheURL error:&error]; if (error) { NSLog(@"%@",error.localizedDescription); } } } -(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{ if (self.isCancelled) { [self setOperationFinished]; } CGFloat progress = (CGFloat)totalBytesWritten/(CGFloat)totalBytesExpectedToWrite; dispatch_async(dispatch_get_main_queue(), ^(void) { self.progressBlock(progress); }); } -(void)setOperationFinished{ [self.session invalidateAndCancel]; [self willChangeValueForKey:@"isFinished"]; [self willChangeValueForKey:@"isExecuting"]; executing = NO; finished = YES; [self didChangeValueForKey:@"isExecuting"]; [self didChangeValueForKey:@"isFinished"]; } @end
相关文章推荐
- 通过Android命令自动编译出build.xml文件
- 【转】理解 Android Build 系统----不错
- js 中return false;return true;return的区别
- CCirculaQueue
- Storyboard里面的几种Segue区别及视图的切换:push,modal,popover,replace和custom
- Leetcode题解(7)L51/N-Queens
- IOS中UUID存放在不同的地方
- 5.3.1 Unique Binary Sear Trees
- 动态计算UITableViewCell高度详解
- UILabel的属性总结
- Dictionary的TryGetValue方法
- easyui动态表头 && 动态添加tabs
- VBox UUID already exists 问题处理
- 解决类型“System.Web.UI.UpdatePanel”不具有名为“Gridview”的公共属性,
- confluent integrated
- Windows Azure 系列-- Azure Queue的操作
- Win10 Build 10135官方32位镜像下载
- GuozhongCrawler系列教程 (5) TransactionRequest详解
- 优化UITableViewCell高度计算的那些事
- CodeIgniter学习笔记二:CI中的query_builder(AR)、连贯操作