Objective-c 多線程操作 自定義NSOperation 模擬下載

寫在前面
弄了下個(gè)人站...防止內(nèi)容再次被鎖定...所有東西都在這里面
welcome~
個(gè)人博客

寫在前面

使用多線程下載圖片,使用內(nèi)存緩存和磁盤緩存。
這里只為理解NSOperation及其派生類
真要應(yīng)用到APP中 請(qǐng)下載成熟的第三方庫(kù)

效果

效果

下載多張圖片時(shí)可控制線程并發(fā)數(shù)

分析

  • 自定義NSOperation 執(zhí)行下載操作
  • 封裝一個(gè)隊(duì)列操作,創(chuàng)建N個(gè)線程,隊(duì)列控制并發(fā)
  • 通過(guò)線程tag查找對(duì)應(yīng)圖片
  • 通過(guò)隊(duì)列tag查找對(duì)應(yīng)隊(duì)列
  • 緩存設(shè)置 內(nèi)存緩存和磁盤緩存
  • 下載完成通過(guò)delegate或block進(jìn)行回調(diào)通知

代碼

  • 通用內(nèi)容 線程完成時(shí)的回調(diào) 、加載圖片時(shí)的回調(diào)
#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;
//線程操作協(xié)議
@protocol DownloadOperationDelegate <NSObject>
//線程下載數(shù)據(jù)完成
- (void)downloadOperationWithData:(NSData*)data withTag:(int)tag;

@end

//下載操作協(xié)議
@protocol DownloadImageDelegate <NSObject>
//圖片回調(diào)
- (void)downloadImageFinishedWith:(UIImage*)image andTag:(int)tag withQueueTag:(int)queueTag;
@end
#endif /* Uinty_h */
  • 自定義NSOperation 下載數(shù)據(jù)
#import <Foundation/Foundation.h>
#import "Uinty.h"
@interface DownloadOperation : NSOperation

//block
@property (nonatomic,copy)DownloadImageDataBlock imageDataBlock;
//標(biāo)識(shí)
@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];
    //模擬耗時(shí)
    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
  • 加載圖片:緩存加載和線程隊(duì)列加載
#import <Foundation/Foundation.h>
#import "DownloadOperation.h"
@interface DownloadImage : NSObject
//加載完成
@property (nonatomic,copy)DownloadImageBlock downloadFinishedBlock;
//加載單張時(shí)使用
@property (nonatomic,copy)NSString *urlStr;
//加載多張時(shí)使用
@property (nonatomic,strong)NSArray *urlArray;
//隊(duì)列線程最大并發(fā)數(shù)
@property (nonatomic,assign)int  maxOperationCount;
//代理
@property (nonatomic,strong)id<DownloadImageDelegate> delegate;
//標(biāo)識(shí)
@property (nonatomic,assign)int tag;
//磁盤緩存 內(nèi)存緩存 單位:M
@property (nonatomic,assign)NSUInteger diskCapacity;
@property (nonatomic,assign)NSUInteger MemoryCapacity;

//初始化傳url數(shù)組
- (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方法 單個(gè)url裝進(jìn)數(shù)組
- (void)setUrlStr:(NSString *)urlStr {
    _urlStr = urlStr;
    _urlArray = [NSArray arrayWithObject:urlStr];
}


#pragma mark - 加載圖片
- (void)starDownloadImage {
    
    //設(shè)置內(nèi)存緩存和磁盤緩存大小
    NSURLCache *URLCache = [[NSURLCache alloc] initWithMemoryCapacity:self.MemoryCapacity * 1024 * 1024 diskCapacity:self.diskCapacity * 1024 * 1024 diskPath:nil];
    [NSURLCache setSharedURLCache:URLCache];

    //加載數(shù)據(jù)
    for (int i = 0; i < self.urlArray.count; i++) {
        int imageTag = self.imageStartTag + i;
        NSString *urlStr = self.urlArray[i];
        //從內(nèi)存緩存中讀取圖片
        UIImage *memoryImage = [self.imageCache objectForKey:urlStr];
        if (memoryImage) {
            //block回調(diào)結(jié)果
            if (self.downloadFinishedBlock) {
                self.downloadFinishedBlock(memoryImage,imageTag,self.tag);
            }
            //代理回調(diào)結(jié)果
            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回調(diào)結(jié)果
            if (self.downloadFinishedBlock) {
                self.downloadFinishedBlock(diskImage,imageTag,self.tag);
            }
            //代理回調(diào)結(jié)果
            if ([self.delegate respondsToSelector:@selector(downloadImageFinishedWith:andTag:withQueueTag:)]) {
                [self.delegate downloadImageFinishedWith:diskImage andTag:imageTag withQueueTag:self.tag];
            }
            continue;
        }
        
        
        //創(chuàng)建線程加載圖片
        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];
        }
    
        //線程回調(diào)結(jié)果
        op.imageDataBlock = ^(NSData *data,int tag){
            UIImage *image = [UIImage imageWithData:data];
            
            //寫入內(nèi)存緩存
            [self.imageCache setObject:image forKey:urlStr];
            //寫入磁盤緩存
            [data writeToFile:imageCachePath atomically:YES];
            
            //block回調(diào)
            if (self.downloadFinishedBlock) {
                NSLog(@"tage ======%d",tag);
                self.downloadFinishedBlock(image,tag,self.tag);
            }
            //代理回調(diào)
            if ([self.delegate respondsToSelector:@selector(downloadImageFinishedWith:andTag:withQueueTag:)]) {
                [self.delegate downloadImageFinishedWith:image andTag:tag withQueueTag:self.tag];
            }
            
        };

    }
    
}

@end

  • 外部調(diào)用及測(cè)試
- (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];
    //設(shè)置并發(fā)數(shù)
    download.maxOperationCount = 3;
    download.tag = 0;
    download.delegate = self;
    /*   block 回調(diào) 結(jié)果
     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 回調(diào) 結(jié)果
     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;
    
}

//通過(guò)代理回調(diào)操作
- (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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容