1 核心知識點
2 使用案例(多圖下載)
3 相關概念和代碼
3.1 NSOperation本身是抽象類,只能使用它的子類。
三個子類分別是:NSInvocationOperation、NSBlockOperation、以及自定義繼承自NSOperation的類
- (1)NSInvocationOperation
//1.封裝操作
/*
第一個參數:目標對象
第二個參數:該操作要調用的方法,最多接受一個參數
第三個參數:調用方法傳遞的參數,如果方法不接受參數,那么該值傳nil
*/
NSInvocationOperation *operation = [[NSInvocationOperation alloc]
initWithTarget:self selector:@selector(run) object:nil];
//2.啟動操作
[operation start];
- (2)NSBlockOperation
//1.封裝操作
/*
NSBlockOperation提供了一個類方法,在該類方法中封裝操作
*/
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
//在主線程中執行
NSLog(@"---download1--%@",[NSThread currentThread]);
}];
//2.追加操作,追加的操作在子線程中執行
[operation addExecutionBlock:^{
NSLog(@"---download2--%@",[NSThread currentThread]);
}];
[operation addExecutionBlock:^{
NSLog(@"---download3--%@",[NSThread currentThread]);
}];
//3.啟動執行操作
[operation start];
- (3) 自定義NSOperation
//如何封裝操作?
//自定義的NSOperation,通過重寫內部的main方法實現封裝操作
-(void)main
{
NSLog(@"--main--%@",[NSThread currentThread]);
}
//如何使用?
//1.實例化一個自定義操作對象
MYOperation *op = [[MYOperation alloc]init];
//2.執行操作
[op start];
3.2 兩個核心概念
- 隊列:NSOperationQueue
主隊列: 通過mainQueue獲得,凡是放到主隊列中的任務都將在主線程執行
非主隊列: 直接alloc init出來的隊列。非主隊列同時具備了并發和串行的功能,通過設置最大并發數屬性來控制任務是并發執行還是串行執行
- 操作:NSOperation
NSOperation和NSOperationQueue結合使用實現多線程并發
3.3 NSOperationQueue的使用
- (1)與自定義NSOperation結合
-(void)customOperation
{
//1.創建隊列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//2.封裝操作
//好處:1.信息隱蔽
//2.代碼復用
MYOperation *op1 = [[MYOperation alloc]init];
MYOperation *op2 = [[MYOperation alloc]init];
//3.添加操作到隊列中
[queue addOperation:op1];
[queue addOperation:op2];
}
- (2)與NSBlockOperation結合
- (void)block
{
//1.創建隊列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//2.封裝操作
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1----%@",[NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"2----%@",[NSThread currentThread]);
}];
[op2 addExecutionBlock:^{
NSLog(@"3----%@",[NSThread currentThread]);
}];
[op2 addExecutionBlock:^{
NSLog(@"4----%@",[NSThread currentThread]);
}];
//3.添加操作到隊列中
[queue addOperation:op1];
[queue addOperation:op2];
//補充:簡便方法
[queue addOperationWithBlock:^{
NSLog(@"5----%@",[NSThread currentThread]);
}];
}
- (3)與NSInvocationOperation結合
- (void)invocation
{
/*
GCD中的隊列:
串行隊列:自己創建的,主隊列
并發隊列:自己創建的,全局并發隊列
NSOperationQueue
主隊列:[NSOperationQueue mainqueue];凡事放在主隊列中的操作都在主線程中執行
非主隊列:[[NSOperationQueue alloc]init],并發和串行,默認是并發執行的
*/
//1.創建隊列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//2.封裝操作
NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download1) object:nil];
NSInvocationOperation *op2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download2) object:nil];
NSInvocationOperation *op3 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download3) object:nil];
//3.把封裝好的操作添加到隊列中
[queue addOperation:op1];//[op1 start]
[queue addOperation:op2];
[queue addOperation:op3];
}
3.4 NSOperation其它用法
- (1)設置最大并發數【控制任務并發和串行】
/1.創建隊列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//2.設置最大并發數
//注意點:該屬性需要在任務添加到隊列中之前進行設置
//該屬性控制隊列是串行執行還是并發執行
//如果最大并發數等于1,那么該隊列是串行的,如果大于1那么是并行的
//系統的最大并發數有個默認的值,為-1,如果該屬性設置為0,那么不會執行任何任務
queue.maxConcurrentOperationCount = 2;
- (2)暫停和恢復以及取消
//設置暫停和恢復
//suspended設置為YES表示暫停,suspended設置為NO表示恢復
//暫停表示不繼續執行隊列中的下一個任務,暫停操作是可以恢復的
if (self.queue.isSuspended) {
self.queue.suspended = NO;
}else
{
self.queue.suspended = YES;
}
//取消隊列里面的所有操作
//取消之后,當前正在執行的操作的下一個操作將不再執行,而且永遠都不在執行,就像后面的所有任務都從隊列里面移除了一樣
//取消操作是不可以恢復的
[self.queue cancelAllOperations];
---------自定義NSOperation取消操作--------------------------
-(void)main
{
//耗時操作1
for (int i = 0; i<1000; i++) {
NSLog(@"任務1-%d--%@",i,[NSThread currentThread]);
}
NSLog(@"+++++++++++++++++++++++++++++++++");
//蘋果官方建議,每當執行完一次耗時操作之后,就查看一下當前隊列是否為取消狀態,如果是,那么就直接退出
//好處是可以提高程序的性能
if (self.isCancelled) {
return;
}
//耗時操作2,操作取消后,后面的操作不會執行
for (int i = 0; i<1000; i++) {
NSLog(@"任務1-%d--%@",i,[NSThread currentThread]);
}
NSLog(@"+++++++++++++++++++++++++++++++++");
}
4 NSOperation實現線程間通信
- (1)開子線程下載圖片
//1.創建隊列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//2.使用簡便方法封裝操作并添加到隊列中
[queue addOperationWithBlock:^{
//3.在該block中下載圖片
NSURL *url = [NSURL URLWithString:@"http://news.51sheyuan.com/uploads/allimg/111001/133442IB-2.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
NSLog(@"下載圖片操作--%@",[NSThread currentThread]);
//4.回到主線程刷新UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.imageView.image = image;
NSLog(@"刷新UI操作---%@",[NSThread currentThread]);
}];
}];
- (2)下載多張圖片合成綜合案例(設置操作依賴)
- (void)download2
{
NSLog(@"----");
//1.創建隊列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//2.封裝操作下載圖片1
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSURL *url = [NSURL URLWithString:@"http://h.hiphotos.baidu.com/zhidao/pic/item/6a63f6246b600c3320b14bb3184c510fd8f9a185.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
//拿到圖片數據
self.image1 = [UIImage imageWithData:data];
}];
//3.封裝操作下載圖片2
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSURL *url = [NSURL URLWithString:@"http://pic.58pic.com/58pic/13/87/82/27Q58PICYje_1024.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
//拿到圖片數據
self.image2 = [UIImage imageWithData:data];
}];
//4.合成圖片
NSBlockOperation *combine = [NSBlockOperation blockOperationWithBlock:^{
//4.1 開啟圖形上下文
UIGraphicsBeginImageContext(CGSizeMake(200, 200));
//4.2 畫image1
[self.image1 drawInRect:CGRectMake(0, 0, 200, 100)];
//4.3 畫image2
[self.image2 drawInRect:CGRectMake(0, 100, 200, 100)];
//4.4 根據圖形上下文拿到圖片數據
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// NSLog(@"%@",image);
//4.5 關閉圖形上下文
UIGraphicsEndImageContext();
//7.回到主線程刷新UI
[[NSOperationQueue mainQueue]addOperationWithBlock:^{
self.imageView.image = image;
NSLog(@"刷新UI---%@",[NSThread currentThread]);
}];
}];
//5.設置操作依賴
[combine addDependency:op1];
[combine addDependency:op2];
//6.添加操作到隊列中執行
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:combine];
}
- (3) 多圖下載綜合案例核心代碼
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//1.設置標志符
static NSString *ID = @"app";
//2.創建cell
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
//3.設置cell的數據
//3.1 拿到該行cell對應的數據
MYItem *cellItem = self.datas[indexPath.row];
//3.2 設置標題
cell.textLabel.text = cellItem.name;
//3.3 設置子標題
cell.detailTextLabel.text = cellItem.download;
//3.4 設置圖片
//查看該圖片在緩存中是否存在,如果存在那么就直接設置,否則查看磁盤緩存是否存在
//如果存在,直接設置圖片&保存一份到內存
//如果不存在,就去下載
UIImage *image = [self.images objectForKey:cellItem.icon];
if (image) {
cell.imageView.image = image;
NSLog(@"從緩存中取出第%zd張圖片",indexPath.row);
}else
{
//嘗試從磁盤中加載二進制數據
NSData *data = [NSData dataWithContentsOfFile:[self getFullPath:cellItem.icon]];
if (data) {
//1.直接設置圖片
UIImage *image = [UIImage imageWithData:data];
cell.imageView.image = image;
//2.保存一份到內存
[self.images setObject:image forKey:cellItem.icon];
NSLog(@"從沙盒中加載第%zd張圖片",indexPath.row);
}else
{
// 下載圖片
//查看當前圖片的下載操作是否已經存在
NSBlockOperation *download = [self.operations objectForKey:cellItem.icon];
if (download) {
NSLog(@"發現該圖片已經在下載,我什么都不做");
}else
{
// 設置占位圖片
cell.imageView.image = [UIImage imageNamed:@"Snip20160111_304"];
download = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:1.0];
NSURL *url = [NSURL URLWithString:cellItem.icon];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
if (image == nil) {
[self.operations removeObjectForKey:cellItem.icon];
return ;
}
//保存到內存緩存中
[self.images setObject:image forKey:cellItem.icon];
NSLog(@"下載%zd張圖片",indexPath.row);
//寫數據到沙盒
[data writeToFile:[self getFullPath:cellItem.icon] atomically:YES];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
//cell.imageView.image = image;
//刷新指定行
[tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationMiddle];
}];
//移除操作
[self.operations removeObjectForKey:cellItem.icon];
}];
//設置操作緩存
[self.operations setObject:download forKey:cellItem.icon];
[self.queue addOperation:download];
}
}
}
//4.返回cell
return cell;
}
-(NSString *)getFullPath:(NSString *)urlStr
{
//caches
NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
//獲得文件名稱
NSString *fileName = [urlStr lastPathComponent];
//拼接文件的全路徑
NSString *fullPath = [caches stringByAppendingPathComponent:fileName];
return fullPath;
}