• Objective-c 多线程操作 自定义NSOperation 模拟下载


    写在前面

    使用多线程下载图片,使用内存缓存和磁盘缓存。
    这里只为理解NSOperation及其派生类
    真要应用到APP中 请下载成熟的第三方库

    效果

    效果

    下载多张图片时可控制线程并发数

    分析

    • 自定义NSOperation 执行下载操作
    • 封装一个队列操作,创建N个线程,队列控制并发
    • 通过线程tag查找对应图片
    • 通过队列tag查找对应队列
    • 缓存设置 内存缓存和磁盘缓存
    • 下载完成通过delegate或block进行回调通知

    代码

    • 通用内容 线程完成时的回调 、加载图片时的回调
    #ifndef Uinty_h
    #define Uinty_h
    #import <UIKit/UIKit.h>
    
    typedef void(^DownloadImageDataBlock)(NSData *data,int tag);
    typedef void(^DownloadImageBlock)(UIImage *image,int tag,int queueTag);
    
    static int const  kImageViewTag = 1990;
    //线程操作协议
    @protocol DownloadOperationDelegate <NSObject>
    //线程下载数据完成
    - (void)downloadOperationWithData:(NSData*)data withTag:(int)tag;
    
    @end
    
    //下载操作协议
    @protocol DownloadImageDelegate <NSObject>
    //图片回调
    - (void)downloadImageFinishedWith:(UIImage*)image andTag:(int)tag withQueueTag:(int)queueTag;
    @end
    #endif /* Uinty_h */
    
    • 自定义NSOperation 下载数据
    #import <Foundation/Foundation.h>
    #import "Uinty.h"
    @interface DownloadOperation : NSOperation
    
    //block
    @property (nonatomic,copy)DownloadImageDataBlock imageDataBlock;
    //标识
    @property (nonatomic,assign)int tag;
    
    //代理
    @property (nonatomic,strong)id<DownloadOperationDelegate> delegate;
    
    //初始化
    - (instancetype)initWithUrlStr:(NSString*)urlStr;
    
    + (instancetype)downloadOperationWithUrlStr:(NSString*)urlStr;
    @end
    
    
    #import "DownloadOperation.h"
    @interface DownloadOperation()
    @property(nonatomic,copy)NSString *urlStr;
    
    @end
    
    @implementation DownloadOperation
    
    - (instancetype)initWithUrlStr:(NSString*)urlStr {
        self = [super init];
        if (self) {
            self.urlStr = urlStr;
        }
        return self;
    }
    
    + (instancetype)downloadOperationWithUrlStr:(NSString*)urlStr {
        return [[DownloadOperation alloc] initWithUrlStr:urlStr];
    }
    
    -(void)main {
    
        NSURL *url = [NSURL URLWithString:self.urlStr];
        NSData *data = [NSData dataWithContentsOfURL:url];
        //模拟耗时
        sleep(1);
        //返回主线程
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            //block通知
            if (self.imageDataBlock) {
                self.imageDataBlock(data,self.tag);
            }
            //代理通知
            if ([self.delegate respondsToSelector:@selector(downloadOperationWithData:withTag:)]) {
                [self.delegate downloadOperationWithData:data withTag:self.tag];
            }
        }];
        }
    @end
    
    • 加载图片:缓存加载和线程队列加载
    #import <Foundation/Foundation.h>
    #import "DownloadOperation.h"
    @interface DownloadImage : NSObject
    //加载完成
    @property (nonatomic,copy)DownloadImageBlock downloadFinishedBlock;
    //加载单张时使用
    @property (nonatomic,copy)NSString *urlStr;
    //加载多张时使用
    @property (nonatomic,strong)NSArray *urlArray;
    //队列线程最大并发数
    @property (nonatomic,assign)int  maxOperationCount;
    //代理
    @property (nonatomic,strong)id<DownloadImageDelegate> delegate;
    //标识
    @property (nonatomic,assign)int tag;
    //磁盘缓存 内存缓存 单位:M
    @property (nonatomic,assign)NSUInteger diskCapacity;
    @property (nonatomic,assign)NSUInteger MemoryCapacity;
    
    //初始化传url数组
    - (instancetype)initWithUrlStrArray:(NSArray<NSString*>*)urlArray withStartTag:(int)startTag ;
    //初始化单url
    - (instancetype)initWithUrlStr:(NSString*)urlStr ;
    //类工厂
    + (instancetype)downloadImageWithUrlStrArray:(NSArray<NSString*>*)urlArray withStartTag:(int)startTag ;
    + (instancetype)downloadImageWithUrlStr:(NSString*)urlStr ;
    
    //开始下载
    - (void)starDownloadImage;
    @end
    
    //
    //  DownloadImage.m
    //  DownloadImageDemo
    //
    //  Created by gongwenkai on 2017/1/3.
    //  Copyright © 2017年 gongwenkai. All rights reserved.
    //
    
    #import "DownloadImage.h"
    #import "DownloadOperation.h"
    
    
    @interface DownloadImage()
    @property (nonatomic,strong)NSOperationQueue *queue;
    @property (nonatomic,strong)NSCache *imageCache;
    @property (nonatomic,copy)NSString *cachePath;
    @property (nonatomic,assign)int imageStartTag;
    
    @end
    
    @implementation DownloadImage
    
    #pragma mark - 懒加载
    - (NSString *)cachePath {
        if (!_cachePath) {
            _cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
        }
        return _cachePath;
    }
    
    - (NSCache *)imageCache {
        if (!_imageCache) {
            _imageCache = [[NSCache alloc] init];
            _imageCache.countLimit = 100;
            
        }
        return _imageCache;
    }
    
    - (NSOperationQueue *)queue {
        if (!_queue) {
            _queue = [[NSOperationQueue alloc] init];
        }
        return _queue;
    }
    
    - (NSUInteger)MemoryCapacity {
        if (!_MemoryCapacity) {
            _MemoryCapacity = 1;
        }
        return _MemoryCapacity;
    }
    
    - (NSUInteger)diskCapacity {
        if (!_diskCapacity) {
            _diskCapacity = 10;
        }
        return _diskCapacity;
    }
    
    - (int)maxOperationCount {
        if (!_maxOperationCount) {
            _maxOperationCount = 2;
        }
        return _maxOperationCount;
    }
    
    #pragma mark - 初始化
    + (instancetype)downloadImageWithUrlStrArray:(NSArray<NSString*>*)urlArray withStartTag:(int)startTag {
        return [[DownloadImage alloc] initWithUrlStrArray:urlArray withStartTag:startTag];
    }
    
    + (instancetype)downloadImageWithUrlStr:(NSString*)urlStr {
        return [[DownloadImage alloc] initWithUrlStr:urlStr];
    }
    
    - (instancetype)initWithUrlStrArray:(NSArray<NSString*>*)urlArray withStartTag:(int)startTag{
        self = [super init];
        if (self) {
            self.urlArray = urlArray;
            self.imageStartTag = startTag;
        }
        return self;
    }
    
    - (instancetype)initWithUrlStr:(NSString*)urlStr {
        self = [super init];
        if (self) {
            self.urlStr = urlStr;
        }
        return self;
    }
    
    //初始化 set方法 单个url装进数组
    - (void)setUrlStr:(NSString *)urlStr {
        _urlStr = urlStr;
        _urlArray = [NSArray arrayWithObject:urlStr];
    }
    
    
    #pragma mark - 加载图片
    - (void)starDownloadImage {
        
        //设置内存缓存和磁盘缓存大小
        NSURLCache *URLCache = [[NSURLCache alloc] initWithMemoryCapacity:self.MemoryCapacity * 1024 * 1024 diskCapacity:self.diskCapacity * 1024 * 1024 diskPath:nil];
        [NSURLCache setSharedURLCache:URLCache];
    
        //加载数据
        for (int i = 0; i < self.urlArray.count; i++) {
            int imageTag = self.imageStartTag + i;
            NSString *urlStr = self.urlArray[i];
            //从内存缓存中读取图片
            UIImage *memoryImage = [self.imageCache objectForKey:urlStr];
            if (memoryImage) {
                //block回调结果
                if (self.downloadFinishedBlock) {
                    self.downloadFinishedBlock(memoryImage,imageTag,self.tag);
                }
                //代理回调结果
                if ([self.delegate respondsToSelector:@selector(downloadImageFinishedWith:andTag:withQueueTag:)]) {
                    [self.delegate downloadImageFinishedWith:memoryImage andTag:imageTag withQueueTag:self.tag];
                }
                continue;
            }
            //从磁盘缓存中读取图片
            NSString *imagePath=[urlStr lastPathComponent];
            NSString *imageCachePath = [self.cachePath stringByAppendingPathComponent:imagePath];
            NSData *data = [NSData dataWithContentsOfFile:imageCachePath];
            if (data) {
                UIImage *diskImage = [UIImage imageWithData:data];
                //block回调结果
                if (self.downloadFinishedBlock) {
                    self.downloadFinishedBlock(diskImage,imageTag,self.tag);
                }
                //代理回调结果
                if ([self.delegate respondsToSelector:@selector(downloadImageFinishedWith:andTag:withQueueTag:)]) {
                    [self.delegate downloadImageFinishedWith:diskImage andTag:imageTag withQueueTag:self.tag];
                }
                continue;
            }
            
            
            //创建线程加载图片
            self.queue.maxConcurrentOperationCount = self.maxOperationCount;
            DownloadOperation *op = [DownloadOperation downloadOperationWithUrlStr:urlStr];
            
            op.tag = i + kImageViewTag;
            if (self.urlArray.count < self.maxOperationCount) {
                [op start];
            }else {
                [self.queue addOperation:op];
            }
        
            //线程回调结果
            op.imageDataBlock = ^(NSData *data,int tag){
                UIImage *image = [UIImage imageWithData:data];
                
                //写入内存缓存
                [self.imageCache setObject:image forKey:urlStr];
                //写入磁盘缓存
                [data writeToFile:imageCachePath atomically:YES];
                
                //block回调
                if (self.downloadFinishedBlock) {
                    NSLog(@"tage ======%d",tag);
                    self.downloadFinishedBlock(image,tag,self.tag);
                }
                //代理回调
                if ([self.delegate respondsToSelector:@selector(downloadImageFinishedWith:andTag:withQueueTag:)]) {
                    [self.delegate downloadImageFinishedWith:image andTag:tag withQueueTag:self.tag];
                }
                
            };
    
        }
        
    }
    
    @end
    
    
    • 外部调用及测试
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        for (int i = 0; i < 6; i++) {
            UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake(0, i*105, 100, 100)];
            imgView.tag = i + kImageViewTag;
            imgView.backgroundColor = [UIColor redColor];
            [self.view addSubview:imgView];
        }
        
        UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake(120, 100, 100, 100)];
        imgView.backgroundColor = [UIColor redColor];
        imgView.tag = 1234;
        [self.view addSubview:imgView];
        NSLog(@"%@",NSHomeDirectory());
    }
    
    
    - (IBAction)clickLoadImages:(id)sender {
        //下载多张图片
        DownloadImage *download = [DownloadImage downloadImageWithUrlStrArray:@[@"http://img.hb.aicdn.com/6e004cb3c5f58f016a57b90f8bbb93d7075453f2efd0-anTckt_fw658",@"http://img.hb.aicdn.com/fb18b522caf2821adb7af96f8656787f8d9bdad31bdec-6f6h4X_fw658",@"http://img.hb.aicdn.com/22e4dfd8d135de7ae0d451e351c00bddf732919920840-BMRw4Z_fw658",@"http://img.hb.aicdn.com/536c96af48b38faca5bcad20ba0ea6aba8929b711e5b4-lvdBlI_fw658",@"http://img.hb.aicdn.com/055e5458bd340a52ca0067f5d7c22b6c3b18d119292ae-QLEsYp_fw658",@"http://img.hb.aicdn.com/b50481ab8a2b4e3587068df0552ebad08409f0b3ca23-8gBQ9x_fw658"] withStartTag:kImageViewTag];
        //设置并发数
        download.maxOperationCount = 3;
        download.tag = 0;
        download.delegate = self;
        /*   block 回调 结果
         download.downloadFinishedBlock = ^(UIImage *image,int tag,int queueTag) {
         UIImageView *img = [self.view viewWithTag:tag];
         img.image = image;
         NSLog(@"jicia====%d",tag);
         };
         */
        //开始下载
        [download starDownloadImage];
    }
    
    - (IBAction)clickLoadSingalImage:(id)sender {
        //单张下载
        DownloadImage *down = [DownloadImage downloadImageWithUrlStr:@"http://img.hb.aicdn.com/b50481ab8a2b4e3587068df0552ebad08409f0b3ca23-8gBQ9x_fw658"];
        down.tag = 1;
        down.delegate = self;
        
        /*   block 回调 结果
         down.downloadFinishedBlock = ^(UIImage *image,int tag,int queueTag) {
         UIImageView *imgV = [self.view viewWithTag:1234];
         imgV.image = image;
         };
         */
        [down starDownloadImage];
        
    }
    - (IBAction)cleanAll:(id)sender {
        
        for (int i = 0; i < 6; i++) {
            UIImageView *imgView = [self.view viewWithTag:i + kImageViewTag];
            imgView.image = nil;
        }
        UIImageView *imgView = [self.view viewWithTag:1234];
        imgView.image = nil;
        
    }
    
    //通过代理回调操作
    - (void)downloadImageFinishedWith:(UIImage*)image andTag:(int)tag withQueueTag:(int)queueTag{
        if (queueTag == 0) {
            UIImageView *img = [self.view viewWithTag:tag];
            img.image = image;
        } else if (queueTag == 1) {
            UIImageView *imgV = [self.view viewWithTag:1234];
            imgV.image = image;
        }
    
    }
    

    DEMO地址

    https://github.com/gongxiaokai/DownloadImageDemo

  • 相关阅读:
    经典布局 ---- 双飞翼
    细嚼浏览器兼容----条件注释判断浏览器版本
    webqq的注册登记和聊天页面--运用jsonp跨域
    Bootstrap框架的要点--栅格系统
    html5橡皮檫特效
    PHP正确获取客户端IP地址
    常用排序算法及Java实现
    Math类中的floor、ceil和round方法
    Java中的动态反射机制和动态代理
    测试
  • 原文地址:https://www.cnblogs.com/gongxiaokai/p/7123830.html
Copyright © 2020-2023  润新知