• iOS开发多线程篇—自定义NSOperation


     

    iOS开发多线程篇—自定义NSOperation

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

    实现效果展示:

     

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

    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中得界面,如下:

    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 : 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文件

    复制代码
     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
    复制代码

    打印查看:

    二、自定义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
    复制代码

    打印查看:

  • 相关阅读:
    spring六种种依赖注入方式
    HibernateDaoSupport 源码
    ServiceStack.Redis常用操作
    ServiceStack.Redis 之 IRedisTypedClient
    ServiceStack.Redis之IRedisClient
    Windows下Redis的安装使用
    为什么要使用SLF4J而不是Log4J
    每天一个linux命令:mkdir
    浅谈Redis及其安装配置
    Solr4.4的安装与配置
  • 原文地址:https://www.cnblogs.com/LiLihongqiang/p/5588303.html
Copyright © 2020-2023  润新知