NSOperationQueue和GCD的隊列類型
- NSOperationQueue
- 主隊列[NSOperationQueue mainQueue]
凡是添加到主隊列中的任務(NSOperation),都會放到主線程中執(zhí)行 - 非主隊列(其他隊列)
[[NSOperationQueue alloc] init].同時包含了:串行、并發(fā)功能添加到這種隊列中的任務(NSOperation),就會自動放到子線程中執(zhí)行
2.GCD
- 并發(fā)隊列
全局、自己創(chuàng)建 - 串行隊列
主隊列、自己創(chuàng)建
NSOperation功能
- 開啟任務
需要執(zhí)行的操作封裝到一個NSOperation對象中,然后將NSOperation對象添NSOperationQueue中系統(tǒng)會自動將NSOperationQueue中的NSOperation取出來,將取出的NSOperation封裝的操作放到一條新線程中執(zhí)行。NSOperation是個抽象類,并不具備封裝操作的能力,必須使用它的子類,系統(tǒng)提供的子類是
NSBlockOperation和NSInvocationOperation
//這里是在主線程執(zhí)行的
self.blockOperation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"塊內執(zhí)行線程 %@",[NSThread currentThread]);
}];
//這里是子線程
[self.blockOperation addExecutionBlock:^{
NSLog(@"這是新加的塊方法 %@",[NSThread currentThread]);
}];
[self.blockOperation start];
//主線程
NSInvocationOperation * op = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(execute) object:nil];
[op start];
- 默認情況下,調用了start方法后并不會開一條新線程去執(zhí)行操作,而是在當前線程同步執(zhí)行操作
只有將NSOperation放到一個NSOperationQueue中,才會異步執(zhí)行操作
對于NSBlockOperation 只要block操作數大于1就會執(zhí)行異步操作。
也可以自定義子類,方法非常簡單只需要繼承NSOperation后將操作寫在main函數就可以。自己創(chuàng)建自動釋放池(因為如果是異步操作,無法訪問主線程的自動釋放池)。
經常通過- (BOOL)isCancelled方法檢測操作是否被取消,對取消做出響應,對應操作比較耗時的時候。防止不能及時退出
NSBlockOperation 只要里面的操作塊大于1就會進行異步操作。
設置依賴
NSOperation之間可以設置依賴來保證執(zhí)行順序,比如一定要讓操作A執(zhí)行完后,才能執(zhí)行操作B,可以這么寫
//設置op1依賴op2,不在一個隊列也無所謂,不能相互依賴
NSBlockOperation * op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"任務1");
}];
NSInvocationOperation * op2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run) object:nil];
[op1 addDependency:op2];
NSOperationQueue * op1queue = [[NSOperationQueue alloc]init];
[op1queue addOperation:op1];
NSOperationQueue * op2queue = [[NSOperationQueue alloc]init];
[op2queue addOperation:op2];
NSOperationQueue
NSOperation可以調用start方法來執(zhí)行任務,但默認是同步執(zhí)行的如果將NSOperation添加到NSOperationQueue(操作隊列)中,系統(tǒng)會自動異步執(zhí)行NSOperation中的操作。
- 添加任務到隊列
- (void)addOperation:(NSOperation *)op;
- (void)addOperationWithBlock:(void (^)(void))block
[queue addOperationWithBlock:^{
NSLog(@"download1 --- %@", [NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"download2 --- %@", [NSThread currentThread]);
}];
執(zhí)行的任務順序不是和添加的順序一樣的,沒有必然關系。添加到隊列里面后任務由隊列調度運行,不用start了。
2.可以設置并發(fā)數目
并發(fā)數目設置為1的時候就是串行隊列,大于1的時候就是并行隊列
- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
- (void)cancelAllOperations;可以中斷隊列當前的所有任務
提示:也可以調用NSOperation的- (void)cancel方法取消單個操作
暫停和恢復隊列
- (void)setSuspended:(BOOL)b;// YES代表暫停隊列,NO代表恢復隊列
- (BOOL)isSuspended;
可以監(jiān)聽一個操作的執(zhí)行完畢
- (void (^)(void))completionBlock;
- (void)setCompletionBlock:(void (^)(void))block;
下面是一個來自小碼哥自己實現圖片緩存的案例
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *ID = @"app";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
XMGApp *app = self.apps[indexPath.row];
cell.textLabel.text = app.name;
cell.detailTextLabel.text = app.download;
// 先從內存緩存中取出圖片
UIImage *image = self.images[app.icon];
if (image) { // 內存中有圖片
cell.imageView.image = image;
} else { // 內存中沒有圖片
// 獲得Library/Caches文件夾
NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
// 獲得文件名
NSString *filename = [app.icon lastPathComponent];
// 計算出文件的全路徑
NSString *file = [cachesPath stringByAppendingPathComponent:filename];
// 加載沙盒的文件數據
NSData *data = [NSData dataWithContentsOfFile:file];
if (data) { // 直接利用沙盒中圖片
UIImage *image = [UIImage imageWithData:data];
cell.imageView.image = image;
// 存到字典中
self.images[app.icon] = image;
} else { // 下載圖片
[self.queue addOperationWithBlock:^{
// 下載圖片
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:app.icon]];
UIImage *image = [UIImage imageWithData:data];
[NSThread sleepForTimeInterval:1.0];
// 回到主線程顯示圖片
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
cell.imageView.image = image;
}];
// 存到字典中
self.images[app.icon] = image;
// 將圖片文件數據寫入沙盒中
[data writeToFile:file atomically:YES];
}];
}
}
return cell;
}