@qidiandasheng
2016-09-04T16:06:55.000000Z
字数 5830
阅读 2460
第三方库
SDWebImage
库里有个SDWebImageDownloaderOperation
是专门用来做多线程下的图片下载操作的。我们现在来看一下这个类是怎么设计的。
我们先来看一下它是如何用的,然后再看一下它的内部结构。
在SDWebImage
中的SDWebImageDownloader
下载类中用到了这个SDWebImageDownloaderOperation
下载操作类。
SDWebImageDownloader
初始化的时候创建一个_operationClass
对象。下面是代码,我这里只列出跟operation
相关的一部分代码:
- (id)init {
if ((self = [super init])) {
//获取SDWebImageDownloaderOperation类对象
_operationClass = [SDWebImageDownloaderOperation class];
//设置执行的规则是FIFO(先进先出)还是LIFO(后进先出)
_executionOrder = SDWebImageDownloaderFIFOExecutionOrder;
//创建Operation队列
_downloadQueue = [NSOperationQueue new];
//设置队列的最大并发数
_downloadQueue.maxConcurrentOperationCount = 6;
}
return self;
}
- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url
options:(SDWebImageDownloaderOptions)options
progress:(SDWebImageDownloaderProgressBlock)progressBlock
completed:(SDWebImageDownloaderCompletedBlock)completedBlock;
url:
下载的图片的url
options:
一些下载的选项,主要涉及到下载的优先级、缓存、后台任务执行、cookie处理以认证几个方面。
progressBlock:
下载的进度block
completedBlock:
下载完成的block
return: 返回一个遵循<SDWebImageOperation>
协议的SDWebImageDownloaderOperation
实例对象。
下面就写几个在此函数中跟operation
相关的代码:
//创建`SDWebImageDownloaderOperation`实例对象。
operation = [[wself.operationClass alloc] initWithRequest:...];
//是否压缩图片
operation.shouldDecompressImages = wself.shouldDecompressImages;
//下载凭证相关的东西
if (wself.urlCredential) {
operation.credential = wself.urlCredential;
} else if (wself.username && wself.password) {
operation.credential = [NSURLCredential credentialWithUser:wself.username password:wself.password persistence:NSURLCredentialPersistenceForSession];
}
//operation的优先级
if (options & SDWebImageDownloaderHighPriority) {
operation.queuePriority = NSOperationQueuePriorityHigh;
} else if (options & SDWebImageDownloaderLowPriority) {
operation.queuePriority = NSOperationQueuePriorityLow;
}
//加入到队列中
[wself.downloadQueue addOperation:operation];
//判断设置的执行规则,如果是LIFO(后进先出),那就使用addDependency方法,也就是之前最后一个添加的操作(lastAddedOperation)要依赖于最新这个添加进来的操作(operation),也就是要等最新的这个执行完了,之前那个才能执行。
if (wself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) {
// Emulate LIFO execution order by systematically adding new operations as last operation's dependency
[wself.lastAddedOperation addDependency:operation];
wself.lastAddedOperation = operation;
}
public
NSURLRequest *request;
request请求
NSURLSessionTask *dataTask;
一个url下载任务
BOOL shouldDecompressImages
是否压缩图片
NSURLCredential *credential;
url凭证
SDWebImageDownloaderOptions options;
一些下载选项,主要涉及到下载的优先级、缓存、后台任务执行、cookie处理以认证几个方面。
NSInteger expectedSize;
将要下载的图片的实际大小
NSURLResponse *response;
url请求的回应
private
SDWebImageDownloaderProgressBlock progressBlock;
下载进度的回调
SDWebImageDownloaderCompletedBlock completedBlock;
下载完成的回调
SDWebImageNoParamsBlock cancelBlock;
取消的回调
BOOL executing;
是否正在执行
BOOL finished;
是否完成
NSMutableData *imageData;
图片数据
NSURLSession *ownedSession;
url连接对象
NSURLSessionTask *dataTask;
session任务
NSThread *thread;
线程
size_t width, height;
图片的宽高
UIImageOrientation orientation;
图片的方向
BOOL responseFromCached;
标记是否从URLCache缓存中读取的
里面主要就是一些上面的属性及一些变量的初始化。
- (id)initWithRequest:(NSURLRequest *)request
inSession:(NSURLSession *)session
options:(SDWebImageDownloaderOptions)options
progress:(SDWebImageDownloaderProgressBlock)progressBlock
completed:(SDWebImageDownloaderCompletedBlock)completedBlock
cancelled:(SDWebImageNoParamsBlock)cancelBlock;
这是operation开始相关的操作
- (void)start {
@synchronized (self) {
//判断操作是否取消,如果取消则直接return不执行之后的请求。
if (self.isCancelled) {
self.finished = YES;
[self reset];
return;
}
//创建session
NSURLSession *session = self.unownedSession;
if (!self.unownedSession) {
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
sessionConfig.timeoutIntervalForRequest = 15;
self.ownedSession = [NSURLSession sessionWithConfiguration:sessionConfig
delegate:self
delegateQueue:nil];
session = self.ownedSession;
}
//创建下载任务
self.dataTask = [session dataTaskWithRequest:self.request];
//标记正在执行
self.executing = YES;
//得到当前的线程
self.thread = [NSThread currentThread];
}
//下载任务开始执行
[self.dataTask resume];
//判断是否有这个下载任务,如果有progressBlock从0开始执行,然后发送开始下载的通知。如果没有则直接completedBlock返回错误。
if (self.dataTask) {
if (self.progressBlock) {
self.progressBlock(0, NSURLResponseUnknownLength);
}
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStartNotification object:self];
});
}
else {
if (self.completedBlock) {
self.completedBlock(nil, nil, [NSError errorWithDomain:NSURLErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Connection can't be initialized"}], YES);
}
}
这是取消operation相关的操作
- (void)cancel {
@synchronized (self) {
if (self.thread) {
[self performSelector:@selector(cancelInternalAndStop) onThread:self.thread withObject:nil waitUntilDone:NO];
}
else {
[self cancelInternal];
}
}
}
- (void)cancelInternalAndStop {
if (self.isFinished) return;
[self cancelInternal];
}
- (void)cancelInternal {
if (self.isFinished) return;
[super cancel];
if (self.cancelBlock) self.cancelBlock();
if (self.dataTask) {
[self.dataTask cancel];
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:self];
});
// As we cancelled the connection, its callback won't be called and thus won't
// maintain the isFinished and isExecuting flags.
if (self.isExecuting) self.executing = NO;
if (!self.isFinished) self.finished = YES;
}
[self reset];
}
这是一些重置及设置实例变量的方法
- (void)done {
self.finished = YES;
self.executing = NO;
[self reset];
}
- (void)reset {
self.cancelBlock = nil;
self.completedBlock = nil;
self.progressBlock = nil;
self.dataTask = nil;
self.imageData = nil;
self.thread = nil;
if (self.ownedSession) {
[self.ownedSession invalidateAndCancel];
self.ownedSession = nil;
}
}
- (void)setFinished:(BOOL)finished {
[self willChangeValueForKey:@"isFinished"];
_finished = finished;
[self didChangeValueForKey:@"isFinished"];
}
- (void)setExecuting:(BOOL)executing {
[self willChangeValueForKey:@"isExecuting"];
_executing = executing;
[self didChangeValueForKey:@"isExecuting"];
}
//定义一个并发操作,重写这个方法并且返回YES
- (BOOL)isConcurrent {
return YES;
}
然后就是一些NSURLSessionDataDelegate
和NSURLSessionTaskDelegate
对应的回调方法
为什么要创建SDWebImageDownloaderOperation
类对象