iOS开发之自定义NSOperation

本文主要介绍通过自定义NSOperation实现图片异步下载功能。下面直接上代码。

1.    先把程序的框架搭建起来:

window的主视图式导航栏控制器UINavigationController,导航栏控制器UINavigationController的根视图是UITableView。效果如下:


2.    向UITableView上设置数据,这里加载的是plist文件:

3.    将plist文件中的数据转化成模型,创建模型HXApp。

HXApp.h

#import <Foundation/Foundation.h>

@interface HXApp : NSObject

/**
 *  应用名称
 */
@property (nonatomic, copy) NSString *name;
/**
 *  应用图标
 */
@property (nonatomic, copy) NSString *icon;
/**
 *  应用下载量
 */
@property (nonatomic, copy) NSString *download;

+ (instancetype)appWithDict:(NSDictionary *)dict;
- (instancetype)initWithDict:(NSDictionary *)dict;

@end
HXApp.m

#import "HXApp.h"

@implementation HXApp

+ (instancetype)appWithDict:(NSDictionary *)dict {
    return [[self alloc] initWithDict:dict];
}

- (instancetype)initWithDict:(NSDictionary *)dict {
    HXApp *app = [[HXApp alloc] init];
    
    [app setValuesForKeysWithDictionary:dict];
    
    return app;
}

@end

4.    在控制器中读取plist文件数据,创建一个模型数组apps(懒加载),转化成模型后添加到数组中;

- (NSMutableArray *)apps {
    if (_apps == nil) {
        
        // 读取数据
        NSArray *appArray = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"apps.plist" ofType:nil]];
        
        // 将数据中的字典转化成模型
        NSMutableArray *apps = [NSMutableArray array];
        for (NSDictionary *appDict in appArray) {
            HXApp *app = [HXApp appWithDict:appDict];
            [apps addObject:app];
        }
        _apps = apps;
        
    }
    return _apps;
}

5.    实现tableView的数据源方法:

#pragma mark - tableView datasource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.apps.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    // 创建cell
    static NSString *identifier = @"APP";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifier];
    }
 
    // 设置数据
    HXApp *app = self.apps[indexPath.row];
    cell.textLabel.text = app.name;
    cell.detailTextLabel.text = app.download;
   
    <span style="color:#FF0000;">// 显示图片(重点介绍该部分)</span>
    return cell;
}

6.    下载图片,我们通过自定义的NSOperation(HXDownLoadOperation)来完成,我们让HXDownLoadOperation专门用来下载图片。HXDownLoadOperation要先下载图片就得知道url,又因为它是下载cell中的图片,还要知道是哪个cell的图片。

HXDownLoadOperation.h

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface HXDownLoadOperation : NSOperation
/**
 *  下载路径
 */
@property (nonatomic, copy) NSString *url;

/**
 *  下载的图片索引
 */
@property (nonatomic, strong) NSIndexPath *indexPath;

@end

7.    在cellForRowAtIndexPath:方法中添加下面代码,并有思路:

<span style="color:#FF0000;">// 显示图片</span><span style="color:#FF0000;">(重点介绍该部分)</span>
    // 1.根据图片url,向字典images中取出对应的image
    UIImage *image = [self.urlsWithImages valueForKey:app.icon];
    // 2.判断image是否存在
    if (image) {// image存在
        cell.imageView.image = image;
    }else {// image不存在
        
        // 占位图片
        cell.imageView.image = [UIImage imageNamed:@"000"];
        
        // 3.下载图片
        // 根据图片url,向字典operations中取出对应的操作operation
        HXDownLoadOperation *operation = [self.urlsWithOperations valueForKey:app.icon];
        // 4.判断operation下载操作是否存在(即是否正在下载该图片)
        if (operation) {// operation下载操作存在(即正在下载该图片,不作任何处理)
            
        }else {// operation下载操作不存在(即还没有下载过该图片)
            // 创建下载操作
            operation = [[HXDownLoadOperation alloc] init];
            // 图片url
            operation.url = app.icon;
            operation.delegate = self;
            operation.indexPath = indexPath;
            // 添加到队列中(异步执行)
            [self.queue addOperation:operation];// 调用该方法会执行start方法(异步),在start方法中会调用队列的main方法
            
            // 将新的下载操作添加到字典operations中
            //            self.urlsWithOperations[app.icon] = operation;
            [self.urlsWithOperations setValue:operation forKey:app.icon];
        }
    }

8.    HXDownLoadOperation知道图片的url和该图片在cell上的位置后,重写main方法,在main方法中下载完图片,要通知cell显示图片,所在此时的HXDownLoadOperation.h文件中

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@class HXDownLoadOperation;

@protocol HXDownLoadOperationDelegate <NSObject>

@optional
/**
 *  执行完系在操作后,会调用该代理方法
 *
 *  @param operation 下载操作
 *  @param image     下载的图片
 */
- (void)downLoadOperation:(HXDownLoadOperation *)operation didFinishedDownLoad:(UIImage *)image;

@end


@interface HXDownLoadOperation : NSOperation

@property (nonatomic, weak) id<HXDownLoadOperationDelegate> delegate;

/**
 *  下载路径
 */
@property (nonatomic, copy) NSString *url;

/**
 *  下载的图片索引
 */
@property (nonatomic, strong) NSIndexPath *indexPath;

@end
HXDownLoadOperation.m

/**
 *  重写main方法,执行下载的操作
 */
- (void)main {
    NSURL *downLoadUrl = [NSURL URLWithString:self.url];
    NSData *imageData = [NSData dataWithContentsOfURL:downLoadUrl];
    UIImage *image = [UIImage imageWithData:imageData];
    
    
    // 下载完成后,通知代理
    if ([self.delegate respondsToSelector:@selector(downLoadOperation:didFinishedDownLoad:)]) {
        
        // 在主线程中通知代理
        dispatch_async(dispatch_get_main_queue(), ^{
            [self.delegate downLoadOperation:self didFinishedDownLoad:image];
        });
    }
}

9.    将控制器设置为HXDownLoadOperation的代理,并实现代理方法:

#pragma mark - HXDownLoadOperationDelegate
- (void)downLoadOperation:(HXDownLoadOperation *)operation didFinishedDownLoad:(UIImage *)image {
    
    // 执行完下载操作,将该下载操作从字典operations中移除(防止下载失败等问题)
    [self.urlsWithOperations removeObjectForKey:operation.url];
    
    // 向字典images中添加下载之后的图片
    if (image) {// 该判断是防止图片路径无效等问题
        self.urlsWithImages[operation.url] = image;
        
        // 刷新表格(局部刷新)
        [self.tableView reloadRowsAtIndexPaths:@[operation.indexPath] withRowAnimation:UITableViewRowAnimationNone];
    }
}

10.    到此图片下载的步骤已经基本完成,下载完的图片不会在重新下载,所以在滚动cell的时候不会在去下载之前的图片。现在对这个项目做一个小优化。

// 细节优化(当滚动表格的时候,停止下载图片,不滚动的时候,再继续下载)
#pragma mark - UIScrollViewDelegate
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    // 当开始拖拽的时候,通知cell停止下载图片
    [self.queue setSuspended:YES];
    
}

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
    // 当停止拖拽的时候,通知cell继续下载图片
    [self.queue setSuspended:NO];
}

最后运行看一下效果:



本文的内容介绍完了,主要是想总结一下自定义NSOperation的步骤,和实现图片下载的思路。其实关于图片下载有一个很牛X的第三方类库:SDWebImage









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值