NSOperation的作用
配合使用NSOperation和NSOperationQueue也能實現多線程編程
NSOperation和NSOperationQueue實現多線程的具體步驟
- 先將需要執行的操作封裝到一個NSOperation對象中
- 然后將NSOperation對象添加到NSOperationQueue中
- 系統會自動將NSOperationQueue中的NSOperation取出來
- 將取出的NSOperation封裝的操作放到一條新線程中執行
注意:
- NSOperation是個抽象類,并不具備封裝操作的能力,必須使用它的子類
- 使用NSOperation子類的方式有3種:
- NSInvocationOperation
- NSBlockOperation
- 自定義子類繼承NSOperation,實現內部相應的方法
NSInvocationOperation類
- 創建NSInvocationOperation對象
-(id)initWithTarget:(id)targetselector:(SEL)selobject:(id)arg;
- 調用start方法開始執行操作
-(void)start;
一旦執行操作,就會調用target的sel方法
- 代碼:
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
[op start];
- (void)run
{
NSLog(@"------%@", [NSThread currentThread]);
}
- 注意:
- 默認情況下,調用了start方法后并不會開一條新線程去執行操作,而是在當前線程同步執行操作
- 只有將NSOperation放到一個NSOperationQueue中,才會異步執行操作
- 此類僅當了解,在開發中并不常用
NSBlockOperation類
- 創建NSBlockOperation對象
+(id)blockOperationWithBlock:(void(^)(void))block;
- 通過addExecutionBlock:方法添加更多的操作
-(void)addExecutionBlock:(void(^)(void))block;
- 代碼:
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
// 在主線程
NSLog(@"下載1------%@", [NSThread currentThread]);
}];
// 添加額外的任務(在子線程執行)
[op addExecutionBlock:^{
NSLog(@"下載2------%@", [NSThread currentThread]);
}];
[op addExecutionBlock:^{
NSLog(@"下載3------%@", [NSThread currentThread]);
}];
[op addExecutionBlock:^{
NSLog(@"下載4------%@", [NSThread currentThread]);
}];
[op start];
- 注意:只要NSBlockOperation封裝的操作數 >1,就會異步執行操作
NSOperationQueue
NSOperationQueue的作用
NSOperation可以調用start方法來執行任務,但默認是同步執行的
如果將NSOperation添加到NSOperationQueue(操作隊列)中,系統會自動異步執行NSOperation中的操作
添加操作到NSOperationQueue中
-(void)addOperation:(NSOperation*)op;
-(void)addOperationWithBlock:(void(^)(void))block;
- 代碼:
- 方法一:
// 創建隊列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 創建操作
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download1 --- %@", [NSThread currentThread]);
}];
//添加操作到隊列中
[queue addOperation:op1];
- 方法二:
// 創建隊列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//添加操作到隊列中
[queue addOperationWithBlock:^{
NSLog(@"download1 --- %@", [NSThread currentThread]);
}];
NSOperationQueue的隊列類型與GCD的隊列類型對比
- GCD的隊列
- 并發隊列
- 自己創建的
- 全局
- 串行隊列
- 主隊列
- 自己創建的
- NSOperationQueue的隊列
- 主隊列
[NSOperationQueue mainQueue]
- 凡是添加到主隊列中的任務(NSOperation),都會放到主線程中執行
- 非主隊列(其他隊列)
[[NSOperationQueue alloc] init]
- 同時包含了:串行、并發功能
- 添加到這種隊列中的任務(NSOperation),就會自動放到子線程中執行
最大并發數
什么是并發數
同時執行的任務數
比如,同時開3個線程執行3個任務,并發數就是3
最大并發數的相關方法
-(NSInteger)maxConcurrentOperationCount;
-(void)setMaxConcurrentOperationCount:(NSInteger)cnt;
- 代碼
// 創建隊列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // 設置最大并發操作數 // queue.maxConcurrentOperationCount = 2;// 并發隊列 queue.maxConcurrentOperationCount = 1; // 就變成了串行隊列 // 添加操作 [queue addOperationWithBlock:^{ NSLog(@"download1 --- %@", [NSThread currentThread]) }]; [queue addOperationWithBlock:^{ NSLog(@"download2 --- %@", [NSThread currentThread]) }]; [queue addOperationWithBlock:^{ NSLog(@"download3 --- %@", [NSThread currentThread]) }];
隊列的取消、暫停、恢復
- 取消隊列的所有操作
-(void)cancelAllOperations;
提示:也可以調用NSOperation的-(void)cancel方法取消單個操作
- 暫停和恢復隊列
-(void)setSuspended:(BOOL)b;// YES代表暫停隊列,NO代表恢復隊列
- (BOOL)isSuspended
- 代碼
// 恢復隊列,繼續執行
// self.queue.suspended = NO;
// 暫停(掛起)隊列,暫停執行
// self.queue.suspended = YES;
// 取消隊列的所有操作
[self.queue cancelAllOperations];
操作依賴
- NSOperation之間可以設置依賴來保證執行順序
- 比如一定要讓操作A執行完后,才能執行操作B,可以這么寫
[operationB addDependency:operationA];// 操作B依賴于操作A
- 也可以在不同queue的NSOperation之間創建依賴關系
- 代碼
// 創建隊列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 添加操作
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download1----%@", [NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download2----%@", [NSThread currentThread]);
}];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download3----%@", [NSThread currentThread]);
}];
// 設置依賴(保證op3在op1和op2都執行完之后再執行)
[op3 addDependency:op1];
[op3 addDependency:op2];
- 注意:不能相互依賴
- 比如A依賴B,B依賴A
線程之間的通信
- 舉例:在子線程下載圖片,再回答到主線程在imageView添加圖片
- 看代碼:
[[[NSOperationQueue alloc] init] addOperationWithBlock:^{
// 圖片的網絡路徑
NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
// 加載圖片
NSData *data = [NSData dataWithContentsOfURL:url];
// 生成圖片
UIImage *image = [UIImage imageWithData:data];
// 回到主線程
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.imageView.image = image;
}];
}];
操作的監聽
- 可以監聽一個操作的執行完畢
-(void(^)(void))completionBlock;
-(void)setCompletionBlock:(void(^)(void))block;
自定義NSOperation
- 自定義NSOperation的步驟很簡單
- 重寫-(void)main方法,在里面實現想執行的任務
- 重寫-(void)main方法的注意點
- 自己創建自動釋放池(因為如果是異步操作,無法訪問主線程的自動釋放池)
- 經常通過-(BOOL)isCancelled方法檢測操作是否被取消,對取消做出響應