• 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

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

     

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

     

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

    YYappModel.h文件

    //
    //  YYappModel.h
    //  01-自定义Operation
    //
    //  Created by apple on 14-6-26.
    //  Copyright (c) 2014年 itcase. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    
    @interface YYappModel : NSObject
    /**
     *应用名称
     */
    @property(nonatomic,copy)NSString *name;
    /**
     *  应用图片
     */
    @property(nonatomic,copy)NSString *icon;
    /**
     *  应用的下载量
     */
    @property(nonatomic,copy)NSString *download;
    
    +(instancetype)appModelWithDict:(NSDictionary *)dict;
    -(instancetype)initWithDict:(NSDictionary *)dict;
    @end

    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

    主控制器中得逻辑控制部分,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=[NSData dataWithContentsOfURL:url];
        UIImage *imgae=[UIImage imageWithData:data];
        cell.imageView.image=imgae;
        NSLog(@"完成显示");
        return cell;
    }
    
    @end

    打印查看:

     

    二、自定义NSOperation

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

    1.通过代理

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

    YYdownLoadOperation.h文件

    //
    //  YYdownLoadOperation.h
    //  01-自定义Operation
    //
    //  Created by apple on 14-6-26.
    //  Copyright (c) 2014年 itcase. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    
    #pragma mark-设置代理和代理方法
    @class YYdownLoadOperation;
    @protocol YYdownLoadOperationDelegate <NSObject>
    -(void)downLoadOperation:(YYdownLoadOperation*)operation didFishedDownLoad:(UIImage *)image;
    @end
    
    @interface YYdownLoadOperation : NSOperation
    @property(nonatomic,copy)NSString *url;
    @property(nonatomic,strong)NSIndexPath *indexPath;
    @property(nonatomic,strong)id <YYdownLoadOperationDelegate> delegate;
    @end

    YYdownLoadOperation.m文件

    //
    //  YYdownLoadOperation.m
    //  01-自定义Operation
    //
    //  Created by apple on 14-6-26.
    //  Copyright (c) 2014年 itcase. All rights reserved.
    //
    
    #import "YYdownLoadOperation.h"
    
    @implementation YYdownLoadOperation
    -(void)main
    {
        NSURL *url=[NSURL URLWithString:self.url];
        NSData *data=[NSData dataWithContentsOfURL:url];
        UIImage *imgae=[UIImage imageWithData:data];
        
        NSLog(@"--%@--",[NSThread currentThread]);
        //图片下载完毕后,通知代理
        if ([self.delegate respondsToSelector:@selector(downLoadOperation:didFishedDownLoad:)]) {
            dispatch_async(dispatch_get_main_queue(), ^{//回到主线程,传递数据给代理对象
                 [self.delegate downLoadOperation:self didFishedDownLoad:imgae];
            });
        }
    }
    @end

    主控制器中的业务逻辑:

    //
    //  YYViewController.m
    //  01-自定义Operation
    //
    //  Created by apple on 14-6-26.
    //  Copyright (c) 2014年 itcase. All rights reserved.
    //
    
    #import "YYViewController.h"
    #import "YYappModel.h"
    #import "YYdownLoadOperation.h"
    
    @interface YYViewController ()<YYdownLoadOperationDelegate>
    @property(nonatomic,strong)NSArray *apps;
    @property(nonatomic,strong)NSOperationQueue *queue;
    
    @end
    
    @implementation YYViewController
    #pragma mark- 懒加载apps
    -(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-懒加载queue
    -(NSOperationQueue *)queue
    {
        if (_queue==Nil) {
            _queue=[[NSOperationQueue alloc]init];
            //设置最大并发数为3
            _queue.maxConcurrentOperationCount=3;
        }
        return _queue;
    }
    
    #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=[NSData dataWithContentsOfURL:url];
    //    UIImage *imgae=[UIImage imageWithData:data];
    //    cell.imageView.image=imgae;
        
        //创建一个OPeration对象
        YYdownLoadOperation *operation=[[YYdownLoadOperation alloc]init];
        operation.url=app.icon;
        operation.indexPath=indexPath;
        operation.delegate=self;
        
        //把操作对象添加到队列中在去
        [self.queue addOperation:operation];
    
    //    NSLog(@"完成显示");
        return cell;
    }
    -(void)downLoadOperation:(YYdownLoadOperation *)operation didFishedDownLoad:(UIImage *)image
    {
        //返回图片数据给每行对应的cell的imageview.image
        //取出tableview中indexPath这一行对应的cell
        UITableViewCell *cell=[self.tableView cellForRowAtIndexPath:operation.indexPath];
        //显示图片
        cell.imageView.image=image;
    //    NSLog(@"cell--index--%@---%@",operation.indexPath,[NSThread currentThread]);
        //一定要刷新表格
        [self.tableView reloadData];
        NSLog(@"--%@--",[NSThread currentThread]);
    
    }
    @end

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

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

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

    问题3:保证一个url对应一个image。
    下面对主控制器中得代码进行改进:
    //
    //  YYViewController.m
    //  01-自定义Operation
    //
    //  Created by apple on 14-6-26.
    //  Copyright (c) 2014年 itcase. All rights reserved.
    //
    
    #import "YYViewController.h"
    #import "YYappModel.h"
    #import "YYdownLoadOperation.h"
    
    @interface YYViewController ()<YYdownLoadOperationDelegate>
    @property(nonatomic,strong)NSArray *apps;
    @property(nonatomic,strong)NSOperationQueue *queue;
    @property(nonatomic,strong)NSMutableDictionary *operations;
    @property(nonatomic,strong)NSMutableDictionary *images;
    
    @end
    
    @implementation YYViewController
    #pragma mark- 懒加载apps
    -(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-懒加载queue
    -(NSOperationQueue *)queue
    {
        if (_queue==Nil) {
            _queue=[[NSOperationQueue alloc]init];
            //设置最大并发数为3
            _queue.maxConcurrentOperationCount=3;
        }
        return _queue;
    }
    
    #pragma mark-懒加载operations
    -(NSMutableDictionary *)operations
    {
        if (_operations==Nil) {
            _operations=[NSMutableDictionary dictionary];
        }
        return _operations;
    }
    
    #pragma mark-懒加载images
    -(NSMutableDictionary *)images
    {
        if (_images==Nil) {
            _images=[NSMutableDictionary dictionary];
        }
        return _images;
    }
    
    #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;
        
        //保证一个url对应一个image对象
        UIImage *image=self.images[app.icon];
        if (image) {//缓存中有图片
            cell.imageView.image=image;
        }else       //  缓存中没有图片,得下载
        {
            //先设置一张占位图片
            cell.imageView.image=[UIImage imageNamed:@"57437179_42489b0"];
            YYdownLoadOperation *operation=self.operations[app.icon];
            if (operation) {//正在下载
                //什么都不做
            }else  //当前没有下载,那就创建操作
            {
                operation=[[YYdownLoadOperation alloc]init];
                operation.url=app.icon;
                operation.indexPath=indexPath;
                operation.delegate=self;
                [self.queue addOperation:operation];//异步下载
                self.operations[app.icon]=operation;
            }
        }
        
    
        return cell;
    }
    -(void)downLoadOperation:(YYdownLoadOperation *)operation didFishedDownLoad:(UIImage *)image
    {
        //1.移除执行完毕的操作
        [self.operations removeObjectForKey:operation.url];
        
        //2.将图片放到缓存中
        self.images[operation.url]=image;
    
        //3.刷新表格(只刷新下载的那一行)
        
        [self.tableView reloadRowsAtIndexPaths:@[operation.indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
        NSLog(@"--%d--%@--",operation.indexPath.row,[NSThread currentThread]);
    
    }
    @end

    打印查看:

     

  • 相关阅读:
    Oracle+Ado.Net(四)
    Oracle+Ado.Net(三)
    json-server 详解
    在线字体图标
    HTML页面模板代码
    CSS样式重置
    WEB前端开发流程总结
    大前端-全栈-node+easyui+express+vue+es6+webpack+react
    大前端全栈CSS3移动端开发
    jQuery学习
  • 原文地址:https://www.cnblogs.com/yipingios/p/5563728.html
Copyright © 2020-2023  润新知