NSOperation使用簡介
1.操作 NSOperation 和操作隊列 NSOperationQueue!
{
1.NSOperation(操作)簡介:
NSOperation: // 本質是對 GCD 的封裝, OC 語言.
NSOperation 和 GCD 的比較:
GCD使用場合:
一些簡單的需求,簡單的多線程操作. //簡單高效
NSOperation使用場合:
各個操作之間有依賴關係,操作需要取消/暫停;需要限制同時執行的線程數量,讓線程在某時刻停止/繼續等.
配合使用 NSOperation和 NSOperationQueue 也可以實現多線程.
2.NSOperation使用:
NSOperation: 抽象類,不能直接使用,需要使用其子類.
抽象類:定義子類共有的屬性和方法.// CAAnimation/CAPropertyAnimation...
兩個常用子類: NSInvocationOperation(調用) 和 NSBlockOperation(塊);
兩者沒有本質區別,後者使用 Block 的形式組織代碼,使用相對方便.
自定義子類繼承自 NSOperation,實現內部相應的方法. // 高級用法
}
2.NSBlockOperation, NSInvocationOperation的簡單使用.
{
1. 創建 NSInvocationOperation 對象
// 創建 NSInvocationOperation
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(longTimeOperation:) object:@"op1"];
//默認情況下,調用 start 方法之後,不會開啓新線程,只會在當前線程執行操作.
[op1 start];
注意:只有將 NSOperation 放到一個 NSOperationQueue 中,纔會異步執行操作.
2. 創建 NSBlockOperation 對象
// 創建 NSBlockOperation
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"下載圖片1---%@",[NSThread currentThread]);
}];
// 添加更多操作
[op2 addExecutionBlock:^{
NSLog(@"下載圖片2---%@",[NSThread currentThread]);
}];
[op2 addExecutionBlock:^{
NSLog(@"下載圖片3---%@",[NSThread currentThread]);
}];
// 只要 NSBlockOperation 中封裝的操作數 > 1, 調用start方法之後就會開啓多條線程併發執行
// 如果 NSBlockOperation 中封裝的操作數 == 1,調用 start 方法之後,不會開啓新線程,只會在當前線程執行操作
[op2 start];
注意: 只要 NSBlockOperation 中封裝的操作數 > 1,就會異步執行這些操作.(將操作添加到 NSOperationQueue中或者直接調用 start方法都會開啓多條線程異步執行).
}
3.將操作添加到隊列中;
{
NSOperation 可以調用 start 方法來執行任務,但默認是同步執行的.
將 NSOperation 添加到 NSOperationQueue(操作隊列) 中,系統會自動異步執行NSOperationQueue中的操作.
1.NSOperationQueue(操作隊列):
<1> 主隊列
[NSOperationQueue mainQueue] //獲取主隊列
添加到"主隊列"中的操作,都會放在主線程執行!
<2>非主隊列
[[NSOperationQueue alloc] init]; //創建非主隊列
添加到"非主隊列"中得操作,都會放在子線程中執行.
2.使用: 添加操作到操作隊列中.
// 創建 NSInvocationOperation 操作
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(longTimeOperation:) object:@"op1"];
// 創建 NSBlockOperation 操作
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"下載圖片1---%@",[NSThread currentThread]);
}];
// 1.創建一個 NSOperationQueue
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 2.將操作添加到隊列中.
[queue addOperation:op1];
[queue addOperation:op2];
注意:另外一種添加操作到隊列中的方法: block
[queue addOperationWithBlock:^{
NSLog(@"下載圖片5---%@",[NSThread currentThread]);
}];
推薦使用: block // 簡單. 自己哪個使用熟練就用哪個.
注意:隊列中任務的執行是無序的.
}
4.常見用法
1.設置操作依賴. 2.設置最大併發數.
{
問題:是否可以讓隊列中的操作有序執行?
回答上問題: 能,設置操作依賴.
1.NSOperation設置操作依賴: // 執行順序: op1,op2,op3;
// 操作op3依賴於操作op2;
[op3 addDependency:op2];
// 操作op2依賴於操作op1;
[op2 addDependency:op1];
注意:不能相互依賴.
2.NSOperationQueue設置最大併發數.
併發數:同時開啓的線程數.
// 創建操作隊列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 設置操作隊列的最大併發數
queue.maxConcurrentOperationCount = 3;
[queue setMaxConcurrentOperationCount:3];
}
1.隊列的取消/暫停/恢復 2.線程間通信. 注意問題:爲毛要取消/恢復隊列? 在什麼時候用?
{
1.NSOperationQueue 的取消/暫停/恢復
// 取消操作 op1. 取消單個操作.
[op1 cancel];
// 取消所有操作,不會再次恢復
[queue cancelAllOperations];
// 暫停所有操作;注意,已經開始的操作不會暫停.
[queue setSuspended:YES];
// 重新開始所有操作
[queue setSuspended:NO];
問:爲毛要取消恢復隊列? 在什麼時候用?
答:1.爲了內存管理,處理內存警告; 2.爲了用戶體驗,保證滾動流暢.
// 接收到內存警告的時候果斷取消隊列中的所有操作
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
[queue cancelAllOperations]; // 取消隊列中的所有任務(不可恢復)
}
// 開始滾動的時候暫停隊列中的任務.
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
[queue setSuspended:YES]; // 暫停隊列中的所有任務
}
// 滾動結束的時候恢復隊列中的任務.
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
[queue setSuspended:NO]; // 恢復隊列中的所有任務
}
2.線程間通信 // 子線程下載圖片,主線程設置圖片.
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperationWithBlock:^{
// 1.異步下載圖片
NSURL *url = [NSURL URLWithString:@"http://d.hiphotos.baidu.com/image/pic/item/37d3d539b6003af3290eaf5d362ac65c1038b652.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
// 2.回到主線程,顯示圖片
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.imageView.image = image;
}];
}];
}
// 定義一個全局的隊列屬性.方便在任何方法中都可以使用這個Queue
@property (nonatomic,strong) NSOperationQueue *queue;
// UI 控件用 weak 和 Strong 都沒有問題.
// 在開發中,基本會見到所有的UI控件都是用 Strong來做的.
// UI控件一般不要用懶加載的方式加載.UI控件與用戶是對應的.UI控件之外的,能用懶加載就用懶加載.
@property (nonatomic,strong) UIButton *button;
// 設置最大併發數.最多同時只能開啓6條線程.
[_queue setMaxConcurrentOperationCount:6];
// 這裏直接在Block 中使用 self 會造成循環引用. Block使用的注意點之一:循環引用.
// __weak typeof(self) wself = self; wself 就是 self 的弱引用寫法.
// GCD中的任務都是封裝在Block中,如果GCD中的Block中出現了 self,會造成循環引用嗎?
//
// dispatch_async(dispatch_get_main_queue(), ^{
// [self test];
// }); 會造成循環引用嗎? 不會造成循環引用,因爲 self 對這個Block 沒有強引用.
// 創建操作
__weak typeof(self) wself = self;
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
// Block中出現self .Block 對 self 是強引用.
[wself test];
}];
// 將操作添加到隊列中.
// self.queue 對 op 就是強引用.
[self.queue addOperation:op];