首页 > 代码库 > iOS多线程技术—自定义NSOperation

iOS多线程技术—自定义NSOperation

iOS多线程技术—自定义NSOperation

一、实现一个简单的tableView显示效果

实现效果展示:

 

代码示例(使用以前在主控制器中进行业务处理的方式)

1.新建一个项目,让控制器继承自UITableViewController。

////  YYViewController.h//  01-自定义Operation////  Created by apple on 14-6-26.//  Copyright (c) 2014年 itcase. All rights reserved.//#import <UIKit/UIKit.h>@interface YYViewController : UITableViewController@end
View Code

 

2.处理storyboard中得界面,如下:

3.根据plist文件,字典转模型

新建一个类,继承自NSObject,作为数据的模型

YYappModel.h文件

 1 // 2 //  YYappModel.h 3 //  01-自定义Operation 4 // 5 //  Created by apple on 14-6-26. 6 //  Copyright (c) 2014年 itcase. All rights reserved. 7 // 8  9 #import <Foundation/Foundation.h>10 11 @interface YYappModel : NSObject12 /**13  *应用名称14  */15 @property(nonatomic,copy)NSString *name;16 /**17  *  应用图片18  */19 @property(nonatomic,copy)NSString *icon;20 /**21  *  应用的下载量22  */23 @property(nonatomic,copy)NSString *download;24 25 +(instancetype)appModelWithDict:(NSDictionary *)dict;26 -(instancetype)initWithDict:(NSDictionary *)dict;27 @end
View Code

 

YYappModel.m文件

////  YYappModel.m//  01-自定义Operation////  Created by apple on 14-6-26.//  Copyright (c) 2014年 itcase. All rights reserved.//#import "YYappModel.h"@implementation YYappModel-(instancetype)initWithDict:(NSDictionary *)dict{    if (self=[super init]) {        [self setValuesForKeysWithDictionary:dict];    }    return self;}//工厂方法+(instancetype)appModelWithDict:(NSDictionary *)dict{    return [[self alloc]initWithDict:dict];}@end
View Code

 

主控制器中得逻辑控制部分,YYViewController.m文件

////  YYViewController.m//  01-自定义Operation////  Created by apple on 14-6-26.//  Copyright (c) 2014年 itcase. All rights reserved.//#import "YYViewController.h"#import "YYappModel.h"@interface YYViewController ()@property(nonatomic,strong)NSArray *apps;@end@implementation YYViewController#pragma mark- 懒加载-(NSArray *)apps{    if (_apps==nil) {        NSString *path=[[NSBundle mainBundle]pathForResource:@"apps.plist" ofType:nil];        NSArray *tempArray=[NSArray arrayWithContentsOfFile:path];                //字典转模型        NSMutableArray *array=[NSMutableArray array];        for (NSDictionary *dict in tempArray) {            YYappModel *app=[YYappModel appModelWithDict:dict];            [array addObject:app];        }        _apps=array;    }    return _apps;}#pragma mark-数据源方法-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{    return self.apps.count;}-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{    static NSString *ID=@"ID";    UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:ID];    if (cell==nil) {        cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];    }    YYappModel *app=self.apps[indexPath.row];    cell.textLabel.text=app.name;    cell.detailTextLabel.text=app.download;        //下载图片数据    NSLog(@"加载图片数据---%@", [NSThread currentThread]);    NSURL *url=[NSURL URLWithString:app.icon];    NSData *data=http://www.mamicode.com/[NSData dataWithContentsOfURL:url];    UIImage *imgae=[UIImage imageWithData:data];    cell.imageView.image=imgae;    NSLog(@"完成显示");    return cell;}@end
View Code

 

打印查看:

二、自定义NSOperation

说明:上面的下载图片数据部分是一个非常耗时的操作,这个操作任务在主线程完成,会严重的影响到用户体验,造成UI卡的现象。下面通过自定义NSOperation,新开线程,让加载图片的任务异步执行。

1.通过代理

在上面的基础上,新建一个类,让其继承自NSOperation。

YYdownLoadOperation.h文件

 1 // 2 //  YYdownLoadOperation.h 3 //  01-自定义Operation 4 // 5 //  Created by apple on 14-6-26. 6 //  Copyright (c) 2014年 itcase. All rights reserved. 7 // 8  9 #import <Foundation/Foundation.h>10 11 #pragma mark-设置代理和代理方法12 @class YYdownLoadOperation;13 @protocol YYdownLoadOperationDelegate <NSObject>14 -(void)downLoadOperation:(YYdownLoadOperation*)operation didFishedDownLoad:(UIImage *)image;15 @end16 17 @interface YYdownLoadOperation : NSOperation18 @property(nonatomic,copy)NSString *url;19 @property(nonatomic,strong)NSIndexPath *indexPath;20 @property(nonatomic,strong)id <YYdownLoadOperationDelegate> delegate;21 @end
View Code

 

YYdownLoadOperation.m文件

 1 // 2 //  YYdownLoadOperation.m 3 //  01-自定义Operation 4 // 5 //  Created by apple on 14-6-26. 6 //  Copyright (c) 2014年 itcase. All rights reserved. 7 // 8  9 #import "YYdownLoadOperation.h"10 11 @implementation YYdownLoadOperation12 -(void)main13 {14     NSURL *url=[NSURL URLWithString:self.url];15     NSData *data=http://www.mamicode.com/[NSData dataWithContentsOfURL:url];16     UIImage *imgae=[UIImage imageWithData:data];17     18     NSLog(@"--%@--",[NSThread currentThread]);19     //图片下载完毕后,通知代理20     if ([self.delegate respondsToSelector:@selector(downLoadOperation:didFishedDownLoad:)]) {21         dispatch_async(dispatch_get_main_queue(), ^{//回到主线程,传递数据给代理对象22              [self.delegate downLoadOperation:self didFishedDownLoad:imgae];23         });24     }25 }26 @end
View Code

 

主控制器中的业务逻辑:

 1 // 2 //  YYViewController.m 3 //  01-自定义Operation 4 // 5 //  Created by apple on 14-6-26. 6 //  Copyright (c) 2014年 itcase. All rights reserved. 7 // 8  9 #import "YYViewController.h"10 #import "YYappModel.h"11 #import "YYdownLoadOperation.h"12 13 @interface YYViewController ()<YYdownLoadOperationDelegate>14 @property(nonatomic,strong)NSArray *apps;15 @property(nonatomic,strong)NSOperationQueue *queue;16 17 @end18 19 @implementation YYViewController20 #pragma mark- 懒加载apps21 -(NSArray *)apps22 {23     if (_apps==nil) {24         NSString *path=[[NSBundle mainBundle]pathForResource:@"apps.plist" ofType:nil];25         NSArray *tempArray=[NSArray arrayWithContentsOfFile:path];26         27         //字典转模型28         NSMutableArray *array=[NSMutableArray array];29         for (NSDictionary *dict in tempArray) {30             YYappModel *app=[YYappModel appModelWithDict:dict];31             [array addObject:app];32         }33         _apps=array;34     }35     return _apps;36 }37 38 #pragma mark-懒加载queue39 -(NSOperationQueue *)queue40 {41     if (_queue==Nil) {42         _queue=[[NSOperationQueue alloc]init];43         //设置最大并发数为344         _queue.maxConcurrentOperationCount=3;45     }46     return _queue;47 }48 49 #pragma mark-数据源方法50 -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section51 {52     return self.apps.count;53 }54 -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath55 {56     static NSString *ID=@"ID";57     UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:ID];58     if (cell==nil) {59         cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];60     }61     YYappModel *app=self.apps[indexPath.row];62     cell.textLabel.text=app.name;63     cell.detailTextLabel.text=app.download;64     65     //下载图片数据66 //    NSLog(@"加载图片数据---%@", [NSThread currentThread]);67 //    NSURL *url=[NSURL URLWithString:app.icon];68 //    NSData *data=http://www.mamicode.com/[NSData dataWithContentsOfURL:url];>69 //    UIImage *imgae=[UIImage imageWithData:data];70 //    cell.imageView.image=imgae;71     72     //创建一个OPeration对象73     YYdownLoadOperation *operation=[[YYdownLoadOperation alloc]init];74     operation.url=app.icon;75     operation.indexPath=indexPath;76     operation.delegate=self;77     78     //把操作对象添加到队列中在去79     [self.queue addOperation:operation];80 81 //    NSLog(@"完成显示");82     return cell;83 }84 -(void)downLoadOperation:(YYdownLoadOperation *)operation didFishedDownLoad:(UIImage *)image85 {86     //返回图片数据给每行对应的cell的imageview.image87     //取出tableview中indexPath这一行对应的cell88     UITableViewCell *cell=[self.tableView cellForRowAtIndexPath:operation.indexPath];89     //显示图片90     cell.imageView.image=image;91 //    NSLog(@"cell--index--%@---%@",operation.indexPath,[NSThread currentThread]);92     //一定要刷新表格93     [self.tableView reloadData];94     NSLog(@"--%@--",[NSThread currentThread]);95 96 }97 @end
View Code

 

说明:通过打印可以发现上面的代码存在很大的问题。

问题1:需要保证一个url对应一个operation对象。

问题2:下载完需要移除。移除执行完毕的操作。

问题3:保证一个url对应一个image。
下面对主控制器中得代码进行改进:
  1 //  2 //  YYViewController.m  3 //  01-自定义Operation  4 //  5 //  Created by apple on 14-6-26.  6 //  Copyright (c) 2014年 itcase. All rights reserved.  7 //  8   9 #import "YYViewController.h" 10 #import "YYappModel.h" 11 #import "YYdownLoadOperation.h" 12  13 @interface YYViewController ()<YYdownLoadOperationDelegate> 14 @property(nonatomic,strong)NSArray *apps; 15 @property(nonatomic,strong)NSOperationQueue *queue; 16 @property(nonatomic,strong)NSMutableDictionary *operations; 17 @property(nonatomic,strong)NSMutableDictionary *images; 18  19 @end 20  21 @implementation YYViewController 22 #pragma mark- 懒加载apps 23 -(NSArray *)apps 24 { 25     if (_apps==nil) { 26         NSString *path=[[NSBundle mainBundle]pathForResource:@"apps.plist" ofType:nil]; 27         NSArray *tempArray=[NSArray arrayWithContentsOfFile:path]; 28          29         //字典转模型 30         NSMutableArray *array=[NSMutableArray array]; 31         for (NSDictionary *dict in tempArray) { 32             YYappModel *app=[YYappModel appModelWithDict:dict]; 33             [array addObject:app]; 34         } 35         _apps=array; 36     } 37     return _apps; 38 } 39  40 #pragma mark-懒加载queue 41 -(NSOperationQueue *)queue 42 { 43     if (_queue==Nil) { 44         _queue=[[NSOperationQueue alloc]init]; 45         //设置最大并发数为3 46         _queue.maxConcurrentOperationCount=3; 47     } 48     return _queue; 49 } 50  51 #pragma mark-懒加载operations 52 -(NSMutableDictionary *)operations 53 { 54     if (_operations==Nil) { 55         _operations=[NSMutableDictionary dictionary]; 56     } 57     return _operations; 58 } 59  60 #pragma mark-懒加载images 61 -(NSMutableDictionary *)images 62 { 63     if (_images==Nil) { 64         _images=[NSMutableDictionary dictionary]; 65     } 66     return _images; 67 } 68  69 #pragma mark-数据源方法 70 -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 71 { 72     return self.apps.count; 73 } 74 -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 75 { 76     static NSString *ID=@"ID"; 77     UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:ID]; 78     if (cell==nil) { 79         cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID]; 80     } 81     YYappModel *app=self.apps[indexPath.row]; 82     cell.textLabel.text=app.name; 83     cell.detailTextLabel.text=app.download; 84      85     //保证一个url对应一个image对象 86     UIImage *image=self.images[app.icon]; 87     if (image) {//缓存中有图片 88         cell.imageView.image=image; 89     }else       //  缓存中没有图片,得下载 90     { 91         //先设置一张占位图片 92         cell.imageView.image=[UIImage imageNamed:@"57437179_42489b0"]; 93         YYdownLoadOperation *operation=self.operations[app.icon]; 94         if (operation) {//正在下载 95             //什么都不做 96         }else  //当前没有下载,那就创建操作 97         { 98             operation=[[YYdownLoadOperation alloc]init]; 99             operation.url=app.icon;100             operation.indexPath=indexPath;101             operation.delegate=self;102             [self.queue addOperation:operation];//异步下载103             self.operations[app.icon]=operation;104         }105     }106     107 108     return cell;109 }110 -(void)downLoadOperation:(YYdownLoadOperation *)operation didFishedDownLoad:(UIImage *)image111 {112     //1.移除执行完毕的操作113     [self.operations removeObjectForKey:operation.url];114     115     //2.将图片放到缓存中116     self.images[operation.url]=image;117 118     //3.刷新表格(只刷新下载的那一行)119     120     [self.tableView reloadRowsAtIndexPaths:@[operation.indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];121     NSLog(@"--%d--%@--",operation.indexPath.row,[NSThread currentThread]);122 123 }124 @end
View Code

 

打印查看:

iOS多线程技术—自定义NSOperation