用NSOperation写下载队列

简介:

用NSOperation写下载队列

 

说明

1. 支持缓存机制

2. 图片都是在主线程中加载

3. 文件名用了md5加密

*这东西被人写烂了,但大伙如果对NSOperation不熟悉的话,可以看看本人的实现.

 

源码

https://github.com/YouXianMing/NSOperationExample


//
//  ImageDownloadOperation.h
//  NSOperationDownloadImage
//
//  Created by YouXianMing on 15/9/7.
//  Copyright (c) 2015年 YouXianMing. All rights reserved.
//

#import <Foundation/Foundation.h>
@class ImageDownloadOperation;

@protocol ImageDownloadOperationDelegate <NSObject>

@required
- (void)imageDownloadOperation:(ImageDownloadOperation *)operation data:(NSData *)data;

@end

@interface ImageDownloadOperation : NSOperation {
    
    BOOL  _executing;
    BOOL  _finished;
}

/**
 *  代理
 */
@property (nonatomic, weak)   id <ImageDownloadOperationDelegate> delegate;

/**
 *  图片地址
 */
@property (nonatomic, strong) NSString *imageUrlString;

/**
 *  便利构造器
 *
 *  @param urlString 图片地址
 *  @param delegate  代理
 *
 *  @return 实例对象
 */
+ (instancetype)operationWithImageUrlString:(NSString *)urlString
                                   delegate:(id <ImageDownloadOperationDelegate>)delegate;

@end


//
//  ImageDownloadOperation.m
//  NSOperationDownloadImage
//
//  Created by YouXianMing on 15/9/7.
//  Copyright (c) 2015年 YouXianMing. All rights reserved.
//

#import "ImageDownloadOperation.h"
#import <CommonCrypto/CommonDigest.h>

@interface ImageDownloadOperation ()

@property (nonatomic, strong) NSURLConnection *connection;
@property (nonatomic, strong) NSString        *md5String;
@property (nonatomic, strong) NSString        *filePathString;

@end

@implementation ImageDownloadOperation

- (void)main {
    
    // 验证图片地址是否为空
    if (_imageUrlString.length <= 0) {
        
        [self delegateEventWithData:nil];
        [self completeOperation];
        
        return;
    }
    
    // 生成文件路径
    self.md5String      = [self MD5HashWithString:_imageUrlString];
    self.filePathString = [self pathWithFileName:self.md5String];
    
    // 文件如果存在则直接读取
    BOOL exist = [[NSFileManager defaultManager] fileExistsAtPath:self.filePathString isDirectory:nil];
    if (exist) {
        
        [self delegateEventWithData:[NSData dataWithContentsOfFile:self.filePathString]];
        [self completeOperation];
        
        return;
    }
    
    NSURL        *url     = [NSURL URLWithString:_imageUrlString];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    self.connection       = [NSURLConnection connectionWithRequest:request delegate:self];
    
    // 让线程不结束
    do {
        
        @autoreleasepool {
            
            [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
            
            if (self.isCancelled) {
                
                [self completeOperation];
            }
        }
        
    } while (self.isExecuting && self.isFinished == NO);
}

#pragma mark - 网络代理
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {

}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    
    [self writeData:data toPath:self.filePathString];
    [self delegateEventWithData:data];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    
    [self completeOperation];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    
    [self delegateEventWithData:nil];
    [self completeOperation];
}

#pragma mark - 
+ (instancetype)operationWithImageUrlString:(NSString *)urlString
                                   delegate:(id <ImageDownloadOperationDelegate>)delegate {

    ImageDownloadOperation *operation = [[ImageDownloadOperation alloc] init];
    operation.delegate                = delegate;
    operation.imageUrlString          = urlString;
    
    return operation;
}

#pragma mark -
- (void)completeOperation {
    
    [self willChangeValueForKey:@"isFinished"];
    [self willChangeValueForKey:@"isExecuting"];
    _executing = NO;
    _finished  = YES;
    [self didChangeValueForKey:@"isExecuting"];
    [self didChangeValueForKey:@"isFinished"];
}

- (void)start {
    
    if ([self isCancelled]) {
        
        [self willChangeValueForKey:@"isFinished"];
        _finished = YES;
        [self didChangeValueForKey:@"isFinished"];
        
        return;
    }
    
    [self willChangeValueForKey:@"isExecuting"];
    [NSThread detachNewThreadSelector:@selector(main) toTarget:self withObject:nil];
    _executing = YES;
    [self didChangeValueForKey:@"isExecuting"];
}

- (BOOL)isExecuting {
    
    return _executing;
}

- (BOOL)isFinished {
    
    return _finished;
}

- (BOOL)isConcurrent {
    
    return YES;
}

#pragma mark -
- (NSString *)MD5HashWithString:(NSString *)string {
    
    CC_MD5_CTX md5;
    
    CC_MD5_Init(&md5);
    CC_MD5_Update(&md5, [string UTF8String], (CC_LONG) [string length]);
    
    unsigned char digest[CC_MD5_DIGEST_LENGTH];
    CC_MD5_Final(digest, &md5);
    
    NSString *s = [NSString stringWithFormat: @"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
                   digest[0],  digest[1],
                   digest[2],  digest[3],
                   digest[4],  digest[5],
                   digest[6],  digest[7],
                   digest[8],  digest[9],
                   digest[10], digest[11],
                   digest[12], digest[13],
                   digest[14], digest[15]];
    
    return s;
}

- (NSString *)pathWithFileName:(NSString *)name {

    NSString *path = [NSString stringWithFormat:@"/Documents/%@", name];
    return [NSHomeDirectory() stringByAppendingPathComponent:path];
}

- (void)delegateEventWithData:(NSData *)data {
    
    if (_delegate && [_delegate respondsToSelector:@selector(imageDownloadOperation:data:)]) {
        
        dispatch_async(dispatch_get_main_queue(), ^{
            
            [_delegate imageDownloadOperation:self data:data];
        });
    }
}

- (void)writeData:(NSData *)data toPath:(NSString *)path {

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        
        [data writeToFile:path atomically:YES];
    });
}

@end

细节


目录
相关文章
|
19天前
|
存储 安全 API
源码解密协程队列和线程队列的实现原理(二)
源码解密协程队列和线程队列的实现原理(二)
27 1
|
19天前
|
存储 运维 API
源码解密协程队列和线程队列的实现原理(一)
源码解密协程队列和线程队列的实现原理(一)
30 1
|
2月前
|
数据采集 Java Python
python 递归锁、信号量、事件、线程队列、进程池和线程池、回调函数、定时器
python 递归锁、信号量、事件、线程队列、进程池和线程池、回调函数、定时器
|
5月前
|
存储 Java
Java实现队列
Java实现队列
32 0
Java实现队列
|
5月前
|
存储 安全 Java
JAVA常用队列类
JAVA常用队列类
|
Java
|
C++
队列类(C++)
构造函数:初始化队列:将初始尺寸保存到 size,将队首 front 和队尾 rear 均置为 0,为动态数组分配内存并将起始地址保存到 element。Dequeue 函数:若队列不空,则从队列中取出元素并保存到 value中,函数值为 true;size 为动态数组的尺寸,front 为队首元素的下标,rear 为队尾元素下一位置的下标,element 为动态数组的起始地址。Clear 函数:清空队列,将 front 和 rear 重置为 0,操作成功,函数值为 true。首先定义数据元素类型。
145 0
|
Java
【自省】你可思考过 AQS 中的同步队列为何这样设计?
【自省】你可思考过 AQS 中的同步队列为何这样设计?
111 0