iOS多线程应用开发中自定义NSOperation类的实例解析

2016-02-19 09:32 25 1 收藏

在这个颜值当道,屌丝闪边的时代,拼不过颜值拼内涵,只有知识丰富才能提升一个人的内在气质和修养,所谓人丑就要多学习,今天图老师给大家分享iOS多线程应用开发中自定义NSOperation类的实例解析,希望可以对大家能有小小的帮助。

【 tulaoshi.com - 编程语言 】

一、实现一个简单的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;

(本文来源于图老师网站,更多请访问http://www.tulaoshi.com/bianchengyuyan/)

@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;
}

(本文来源于图老师网站,更多请访问http://www.tulaoshi.com/bianchengyuyan/)

#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

打印查看:

来源:http://www.tulaoshi.com/n/20160219/1591191.html

延伸阅读
  仅仅依靠Authorware 6.0本身的功能和它所提供的系统函数,来提高Authorware多媒体程序的灵活性是不够的。有些时候,用户需要利用其他的开发工具来生成用户自定义函数,用来拓展程序的功能。本章向大家展示的就是有关于这方面的应用。 1 概述1.1 什么是用户自定义函数 UCD的全名是User Code Dll,是用户自定义函数的缩写。目前任何...
一、概述 1、对于经常使用的控件或类,通常将其分装为一个单独的类来供外界使用,以此达到事半功倍的效果 2、由于分装的类不依赖于其他的类,所以若要使用该类,可直接将该类拖进项目文件即可 3、在进行分装的时候,通常需要用到代理设计模式 二、代理设计模式 1、代理设计模式的组成 客户类 (通常作为代理):通常委托这是角色来完成...
本人工作有一个月多了。对于android很多东西,都有了新的了解或者说真正的掌握。为了让更多的像我这样的小白少走弯路,所以我会坚持将我在工作中遇到的一些比较令我印象深刻的知识点整合出来给大家(顺序是按照我工作到现在的时间来制作的,其实也是想给自己一个记录吧。记录自己一路走来以及以后的路, 至少我想找到曾经的记录都有了)。 第...
开发 WEB应用系统通常都会遇到报表打印问题。简单应用可利用IE的页面打印功能,利用HTML标签控制格式来实现。但复杂的业务型应用系统,报表不仅是组成应用的重要部分,还常常是相当复杂的。现在很多应用系统都要求提供自定义报表的功能——即客户可以自行设计、修改报表。   在 C/S结构系统中,报表问题有很多成熟的解...
标签: 电脑入门
如果你必须根据不同观众的需要剪裁一个演示文稿,那么自定义放映正是你所需要的功能。可以将自定义放映功能想象为同一个演示文稿相对不同观众而言的各种版本无需分别创建文件。 自定义放映可以根据目标观众或讨论主题创建。例如,你可以为一个观众提供演示文稿概要而为另一个观众提供同一演示文稿的详细内容。或者,如果你的演示文稿包括三种...

经验教程

51

收藏

86
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部