iOS開發多線程篇—自定義NSOperation

1、實現一個簡單的tableView顯示效果緩存

實現效果展現:併發

 

代碼示例(使用之前在主控制器中進行業務處理的方式)app

1.新建一個項目,讓控制器繼承自UITableViewController。異步

複製代碼
 1 //
 2 //  YYViewController.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 <UIKit/UIKit.h>
10 
11 @interface YYViewController : UITableViewController
12 
13 @end
複製代碼

2.處理storyboard中得界面,以下:async

3.根據plist文件,字典轉模型atom

新建一個類,繼承自NSObject,做爲數據的模型url

YYappModel.h文件spa

複製代碼
 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 : NSObject
12 /**
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
複製代碼

YYappModel.m文件線程

複製代碼
 1 //
 2 //  YYappModel.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 "YYappModel.h"
10 
11 @implementation YYappModel
12 
13 -(instancetype)initWithDict:(NSDictionary *)dict
14 {
15     if (self=[super init]) {
16         [self setValuesForKeysWithDictionary:dict];
17     }
18     return self;
19 }
20 
21 //工廠方法
22 +(instancetype)appModelWithDict:(NSDictionary *)dict
23 {
24     return [[self alloc]initWithDict:dict];
25 }
26 @end
複製代碼

主控制器中得邏輯控制部分,YYViewController.m文件3d

複製代碼
 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 
12 @interface YYViewController ()
13 @property(nonatomic,strong)NSArray *apps;
14 
15 @end
16 
17 @implementation YYViewController
18 #pragma mark- 懶加載
19 -(NSArray *)apps
20 {
21     if (_apps==nil) {
22         NSString *path=[[NSBundle mainBundle]pathForResource:@"apps.plist" ofType:nil];
23         NSArray *tempArray=[NSArray arrayWithContentsOfFile:path];
24         
25         //字典轉模型
26         NSMutableArray *array=[NSMutableArray array];
27         for (NSDictionary *dict in tempArray) {
28             YYappModel *app=[YYappModel appModelWithDict:dict];
29             [array addObject:app];
30         }
31         _apps=array;
32     }
33     return _apps;
34 }
35 
36 #pragma mark-數據源方法
37 -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
38 {
39     return self.apps.count;
40 }
41 
42 -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
43 {
44     static NSString *ID=@"ID";
45     UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:ID];
46     if (cell==nil) {
47         cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
48     }
49     YYappModel *app=self.apps[indexPath.row];
50     cell.textLabel.text=app.name;
51     cell.detailTextLabel.text=app.download;
52     
53     //下載圖片數據
54     NSLog(@"加載圖片數據---%@", [NSThread currentThread]);
55     NSURL *url=[NSURL URLWithString:app.icon];
56     NSData *data=[NSData dataWithContentsOfURL:url];
57     UIImage *imgae=[UIImage imageWithData:data];
58     cell.imageView.image=imgae;
59     NSLog(@"完成顯示");
60     return cell;
61 }
62 
63 @end
複製代碼

打印查看:

2、自定義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 @end
16 
17 @interface YYdownLoadOperation : NSOperation
18 @property(nonatomic,copy)NSString *url;
19 @property(nonatomic,strong)NSIndexPath *indexPath;
20 @property(nonatomic,strong)id <YYdownLoadOperationDelegate> delegate;
21 @end
複製代碼

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 YYdownLoadOperation
12 -(void)main
13 {
14     NSURL *url=[NSURL URLWithString:self.url];
15     NSData *data=[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
複製代碼

主控制器中的業務邏輯:

複製代碼
 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 @end
18 
19 @implementation YYViewController
20 #pragma mark- 懶加載apps
21 -(NSArray *)apps
22 {
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-懶加載queue
39 -(NSOperationQueue *)queue
40 {
41     if (_queue==Nil) {
42         _queue=[[NSOperationQueue alloc]init];
43         //設置最大併發數爲3
44         _queue.maxConcurrentOperationCount=3;
45     }
46     return _queue;
47 }
48 
49 #pragma mark-數據源方法
50 -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
51 {
52     return self.apps.count;
53 }
54 -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
55 {
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=[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 *)image
85 {
86     //返回圖片數據給每行對應的cell的imageview.image
87     //取出tableview中indexPath這一行對應的cell
88     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
複製代碼

說明:經過打印能夠發現上面的代碼存在很大的問題。

問題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 *)image
111 {
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
複製代碼

打印查看:

相關文章
相關標籤/搜索