NSOperation

NSOperation是一个抽象类,我们不能直接使用这个类,而是用它的子类。

1、使用NSOperation的子类,我们需要重写main方法,并且在这个方法里面不需要调用父类的main方法。

2、任何操作在执行时,首先会调用start方法,start方法会更新操作的状态(过滤操作);

      经过start方法过滤之后,只有正常可执行的操作,就会调用这个main方法

      重写操作的入口方法(main方法),就可以在这个方法里面指定操作执行的任务

3、  main方法默认是在子线程异步执行的。


常见属性:

@property (nullable,copy)void (^completionBlock)(void)NS_AVAILABLE(10_6,4_0);

用法:

这个block会在完成之后调用。


NSOperation异步下载网络图片

基础代码:

#import "ViewController.h"

#import "AFNetworking.h"

#import "AppInfo.h"

#import "HMTableViewCell.h"

@interfaceViewController ()<UITableViewDataSource>

@property(nonatomic,strong)NSMutableArray<AppInfo*>* appList;

@property (weak,nonatomic)IBOutletUITableView *tableView;

@property(nonatomic,strong)NSOperationQueue* queue;

@end

@implementation ViewController

- (void)viewDidLoad {

    [superviewDidLoad];

    // Do any additional setup after loading the view, typically from a nib.

    self.queue=[[NSOperationQueuealloc]init];

    [selfloadData];

    [self.tableViewregisterNib:[UINibnibWithNibName:@"HMCell"bundle:nil]forCellReuseIdentifier:@"cellId"];

    self.tableView.rowHeight=200;

}

- (void)didReceiveMemoryWarning {

    [superdidReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}

-(void)loadData

{

    AFHTTPSessionManager* manager=[AFHTTPSessionManagermanager];

    [manager GET:@"https://raw.githubusercontent.com/qiruihua/image/master/app.json"parameters:nilprogress:nilsuccess:^(NSURLSessionDataTask *_Nonnull task,id _Nullable responseObject) {

        NSMutableArray* temp=[NSMutableArrayarray];

        for (NSMutableDictionary* infoin responseObject) {

            AppInfo* model=[[AppInfoalloc]init];

            [model setValuesForKeysWithDictionary:info];

            [temp addObject:model];

        }

        self.appList=temp;

        [self.tableViewreloadData];

        

    } failure:nil];

}

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section

{

    return_appList.count;

}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

{

    HMTableViewCell* cell=[tableViewdequeueReusableCellWithIdentifier:@"cellId"forIndexPath:indexPath];

    AppInfo* model=_appList[indexPath.row];

    cell.nameLabel.text=model.name;

    cell.downLoadLabel.text=model.download;

   //在建立异步下载操作之前,设置占位图

    cell.iconView.image=[UIImageimageNamed:@"user_default"];

    NSBlockOperation* op=[NSBlockOperationblockOperationWithBlock:^{

        NSLog(@"从网络下载");

        [NSThreadsleepForTimeInterval:0.1];

        NSURL* url=[NSURLURLWithString:model.icon];

        NSData* data=[NSDatadataWithContentsOfURL:url];

        UIImage *image=[UIImageimageWithData:data];

        [[NSOperationQueuemainQueue]addOperationWithBlock:^{

            NSLog(@"%@",[NSThreadcurrentThread]);

            cell.iconView.image=image;

        }];

    }];

    [self.queueaddOperation:op];

    return cell;

    

}

存在的问题:图片每次展示都要重新去下载,浪费用户流量,解决:实现图片缓存-使用字典做图片内存缓存容器


优化

示例:

#import "ViewController.h"

#import "AFNetworking.h"

#import "AppInfo.h"

#import "HMTableViewCell.h"

@interfaceViewController ()<UITableViewDataSource>

@property(nonatomic,strong)NSMutableArray<AppInfo*>* appList;

@property (weak,nonatomic)IBOutletUITableView *tableView;

@property(nonatomic,strong)NSOperationQueue* queue;

//定义一个字典-缓存池

@property(nonatomic,strong)NSMutableDictionary* imageCache;


@end


@implementation ViewController


- (void)viewDidLoad {

    [superviewDidLoad];

    // Do any additional setup after loading the view, typically from a nib.

    self.queue=[[NSOperationQueuealloc]init];

    

    [selfloadData];

    

    [self.tableViewregisterNib:[UINibnibWithNibName:@"HMCell"bundle:nil]forCellReuseIdentifier:@"cellId"];

    

    self.tableView.rowHeight=200;

    

    _imageCache=[[NSMutableDictionaryalloc]init];

    

}


- (void)didReceiveMemoryWarning {

    [superdidReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}


-(void)loadData

{

    AFHTTPSessionManager* manager=[AFHTTPSessionManagermanager];

    

    [manager GET:@"https://raw.githubusercontent.com/qiruihua/image/master/app.json"parameters:nilprogress:nilsuccess:^(NSURLSessionDataTask *_Nonnull task,id _Nullable responseObject) {

        

        NSMutableArray* temp=[NSMutableArrayarray];

        for (NSMutableDictionary* infoin responseObject) {

            AppInfo* model=[[AppInfoalloc]init];

            [model setValuesForKeysWithDictionary:info];

            [temp addObject:model];

        }

        self.appList=temp;

        [self.tableViewreloadData];

        

    } failure:nil];

    

}

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section

{

    return_appList.count;

}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

{

    

    HMTableViewCell* cell=[tableViewdequeueReusableCellWithIdentifier:@"cellId"forIndexPath:indexPath];

    AppInfo* model=_appList[indexPath.row];

    cell.nameLabel.text=model.name;

    cell.downLoadLabel.text=model.download;

    

    //返回指定 key value,若没有这个 key返回 nil.

    UIImage* imageCache=[_imageCacheobjectForKey:model.icon];

    if(imageCache!=nil)

    {

        NSLog(@"内存里有图片,从内存取");

        cell.iconView.image=imageCache;

        return cell;

    }

    

//    //在建立异步下载操作之前,设置占位图

    cell.iconView.image=[UIImageimageNamed:@"user_default"];

    

    NSBlockOperation* op=[NSBlockOperationblockOperationWithBlock:^{

        

        NSLog(@"从网络下载");

        

        [NSThreadsleepForTimeInterval:0.1];

        

        NSURL* url=[NSURLURLWithString:model.icon];

        

        NSData* data=[NSDatadataWithContentsOfURL:url];

        

        UIImage *image=[UIImageimageWithData:data];

    

        [[NSOperationQueuemainQueue]addOperationWithBlock:^{

            

            NSLog(@"%@",[NSThreadcurrentThread]);

            

            cell.iconView.image=image;

            

            if (_imageCache!=nil) {

                [_imageCachesetObject:imageforKey:model.icon];

            }

        }];

    }];

    

    [self.queueaddOperation:op];

    return cell;

}



//问题:当有网络延迟时,有延迟的图片后下载完的,没有延迟的图片先下载完的。在有cell复用的前提下,先下载完的图片先赋值,后下载完的图片

在赋值时会把先下载完的图片覆盖,就造成了图片错行展示。

//解决:刷新对应行,哪一行下载完就只刷新哪一行cell


第二次优化:


-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

{

    HMTableViewCell* cell=[tableViewdequeueReusableCellWithIdentifier:@"cellId"forIndexPath:indexPath];

    AppInfo* model=_appList[indexPath.row];

    cell.nameLabel.text=model.name;

    cell.downLoadLabel.text=model.download;

    UIImage* memImage=[_imageCacheobjectForKey:model.icon];

    if (memImage!=nil) {

        NSLog(@"从内存中加载...%@",model.name);

        cell.iconView.image=memImage;

        return cell;

    }

   //在建立异步下载操作之前,设置占位图

    cell.iconView.image=[UIImageimageNamed:@"user_default"];

    NSBlockOperation* op=[NSBlockOperationblockOperationWithBlock:^{

        NSLog(@"从网络下载");

       //模拟网络延迟:让屏幕之外的图片下载时有严重的延迟,仅仅是为了放大BUG的效果而已

        if(indexPath.row>9)

        {

            [NSThreadsleepForTimeInterval:5.0];

        }

        NSURL* url=[NSURLURLWithString:model.icon];

        NSData* data=[NSDatadataWithContentsOfURL:url];

        UIImage *image=[UIImageimageWithData:data];

        //回到主线程给iconImageView赋值

        [[NSOperationQueuemainQueue]addOperationWithBlock:^{

            NSLog(@"%@",[NSThreadcurrentThread]);

//            cell.iconView.image=image;

            if (image!=nil) {

                [_imageCachesetObject:imageforKey:model.icon];

                [self.tableViewreloadRowsAtIndexPaths:@[indexPath]withRowAnimation:UITableViewRowAnimationNone];

            }

        }];

    }];

    [self.queueaddOperation:op];

    return cell;

    

}



//问题:

当在10s之类快速的上下滚动列表时,会出现多个下载操作下载同一张图片的问题

 分析:

 1、每次展示一张图片,就会建立对应的下载操作去下载cell上的图片

 2、但是,当有网络延迟时,如果这个cell上的图片还没有下载完成,那么这个cell每出现

 一次就会建立一个下载操作去重复的下载同一张图片

 解决:操作缓存池-类似图片缓存池

 1、在建立下载操作之前先判断这个图片对应的下载操作有没有,如果有就不再建立下载操作,

 反之,就建立新的下载操作去下载这个图片

第三次优化:

#import "ViewController.h"

#import "AFNetworking.h"

#import "AppInfo.h"

#import "HMTableViewCell.h"

@interfaceViewController ()<UITableViewDataSource>

@property(nonatomic,strong)NSMutableArray<AppInfo*>* appList;

@property (weak,nonatomic)IBOutletUITableView *tableView;

@property(nonatomic,strong)NSOperationQueue* queue;

@property(nonatomic,strong)NSMutableDictionary* imageCache;

@end

@implementation ViewController

{

    NSMutableDictionary* _opCache;

}

- (void)viewDidLoad {

    [superviewDidLoad];

    // Do any additional setup after loading the view, typically from a nib.

    self.queue=[[NSOperationQueuealloc]init];   

    [selfloadData];

    [self.tableViewregisterNib:[UINibnibWithNibName:@"HMCell"bundle:nil]forCellReuseIdentifier:@"cellId"];

    _imageCache =[[NSMutableDictionaryalloc]init];

    _opCache=[[NSMutableDictionaryalloc]init];

    self.tableView.rowHeight=200;

}


//处理内存警告

- (void)didReceiveMemoryWarning {

    [superdidReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

    [_imageCacheremoveAllObjects];

    [_opCacheremoveAllObjects];

    [_queuecancelAllOperations];

}

-(void)loadData

{

    AFHTTPSessionManager* manager=[AFHTTPSessionManagermanager];

    [manager GET:@"https://raw.githubusercontent.com/qiruihua/image/master/app.json"parameters:nilprogress:nilsuccess:^(NSURLSessionDataTask *_Nonnull task,id _Nullable responseObject) {

        NSMutableArray* temp=[NSMutableArrayarray];

        for (NSMutableDictionary* infoin responseObject) {

            AppInfo* model=[[AppInfoalloc]init];

            [model setValuesForKeysWithDictionary:info];

            [temp addObject:model];

        }

        self.appList=temp;

        [self.tableViewreloadData];

    } failure:nil];

}

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section

{

    return_appList.count;

}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

{

    HMTableViewCell* cell=[tableViewdequeueReusableCellWithIdentifier:@"cellId"forIndexPath:indexPath];

    AppInfo* model=_appList[indexPath.row];

    cell.nameLabel.text=model.name;

    cell.downLoadLabel.text=model.download;

    UIImage* memImage=[_imageCacheobjectForKey:model.icon];

    if (memImage!=nil) {

        NSLog(@"从内存中加载...%@",model.name);

        cell.iconView.image=memImage;

        return cell;

    }

   //在建立下载操作之前,判断要建立的下载操作在缓存池有没有

    if ([_opCacheobjectForKey:model.icon]) {

        NSLog(@"正在下载...%@",model.name);

        return cell;

    }

   //在建立异步下载操作之前,设置占位图

    cell.iconView.image=[UIImageimageNamed:@"user_default"];

    NSBlockOperation* op=[NSBlockOperationblockOperationWithBlock:^{

        NSLog(@"从网络下载");

       //模拟网络延迟:让屏幕之外的图片下载时有严重的延迟,仅仅是为了放大BUG的效果而已

        if(indexPath.row>9)

        {

            [NSThreadsleepForTimeInterval:5.0];

        }

        NSURL* url=[NSURLURLWithString:model.icon];

        NSData* data=[NSDatadataWithContentsOfURL:url];

        UIImage *image=[UIImageimageWithData:data];

        //回到主线程给iconImageView赋值

        [[NSOperationQueuemainQueue]addOperationWithBlock:^{

            NSLog(@"%@",[NSThreadcurrentThread]);

//            cell.iconView.image=image;

            if (image!=nil) {

                [_imageCachesetObject:imageforKey:model.icon];

                [self.tableViewreloadRowsAtIndexPaths:@[indexPath]withRowAnimation:UITableViewRowAnimationNone];

            }

           //图片下载完成,把操作从操作缓存池移除

            [_opCacheremoveObjectForKey:model.icon];

        }];

    }];

   //把下载操作添加到操作缓存池

    [_opCachesetObject:opforKey:model.icon];

    [self.queueaddOperation:op];

    return cell;

    

}

//问题:当程序再次启动时,内存缓存失效,图片依然需要重新下载

// 解决:沙盒缓存

第四次优化

准备分类:

#import <Foundation/Foundation.h>


@interface NSString (path)

//获取cache文件缓存路径

-(NSString*)appendCachePath;

@end

----------------

#import "NSString+path.h"

@implementation NSString (path)

-(NSString*)appendCachePath

{

    NSString* path=NSSearchPathForDirectoriesInDomains(NSCachesDirectory,NSUserDomainMask,YES).lastObject;

    NSString* name=[selflastPathComponent];

    NSString* filePath=[pathstringByAppendingPathComponent:name];

    return filePath;

}

@end

-------------

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

{

    HMTableViewCell* cell=[tableViewdequeueReusableCellWithIdentifier:@"cellId"forIndexPath:indexPath];

    AppInfo* model=_appList[indexPath.row];

    cell.nameLabel.text=model.name;

    cell.downLoadLabel.text=model.download;

    UIImage* memImage=[_imageCacheobjectForKey:model.icon];

    if (memImage!=nil) {

        NSLog(@"从内存中加载...%@",model.name);

        cell.iconView.image=memImage;

        return cell;

    }

   //在建立操作之前,内存缓存判断之后,判断有没有沙盒缓存

    UIImage* cacheImage=[UIImageimageWithContentsOfFile:[model.iconappendCachePath]];

    if (cacheImage!=nil) {

        NSLog(@"从沙盒中加载... %@",model.name);

        [_imageCachesetObject:cacheImageforKey:model.icon];

        cell.iconView.image=cacheImage;

        return cell;

    }

   //在建立下载操作之前,判断要建立的下载操作在缓存池有没有

    if ([_opCacheobjectForKey:model.icon]) {

        NSLog(@"正在下载...%@",model.name);

        return cell;

    }

   //在建立异步下载操作之前,设置占位图

    cell.iconView.image=[UIImageimageNamed:@"user_default"];

    NSBlockOperation* op=[NSBlockOperationblockOperationWithBlock:^{

        NSLog(@"从网络下载");

       //模拟网络延迟:让屏幕之外的图片下载时有严重的延迟,仅仅是为了放大BUG的效果而已

        if(indexPath.row>9)

        {

            [NSThreadsleepForTimeInterval:5.0];

        }

        NSURL* url=[NSURLURLWithString:model.icon];

        NSData* data=[NSDatadataWithContentsOfURL:url];

        UIImage *image=[UIImageimageWithData:data];

        if (image) {

            

            //将图片保存到内存缓存池

            [data writeToFile:[model.iconappendCachePath]atomically:YES];

        }

        //回到主线程给iconImageView赋值

        [[NSOperationQueuemainQueue]addOperationWithBlock:^{

            NSLog(@"%@",[NSThreadcurrentThread]);

//            cell.iconView.image=image;

            if (image!=nil) {

                [_imageCachesetObject:imageforKey:model.icon];

                [self.tableViewreloadRowsAtIndexPaths:@[indexPath]withRowAnimation:UITableViewRowAnimationNone];

            }

           //图片下载完成,把操作从操作缓存池移除

            [_opCacheremoveObjectForKey:model.icon];

        }];

    }];

   //把下载操作添加到操作缓存池

    [_opCachesetObject:opforKey:model.icon];

    [self.queueaddOperation:op];

    return cell;

}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值