一、實現一個簡單的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
打印查看: