[关闭]
@qidiandasheng 2016-09-04T08:06:55.000000Z 字数 5830 阅读 2263

SDWebImageDownloaderOperation分析

第三方库


SDWebImage库里有个SDWebImageDownloaderOperation是专门用来做多线程下的图片下载操作的。我们现在来看一下这个类是怎么设计的。

如何使用

我们先来看一下它是如何用的,然后再看一下它的内部结构。

SDWebImage中的SDWebImageDownloader下载类中用到了这个SDWebImageDownloaderOperation下载操作类。

下面是代码,我这里只列出跟operation相关的一部分代码:

  1. - (id)init {
  2. if ((self = [super init])) {
  3. //获取SDWebImageDownloaderOperation类对象
  4. _operationClass = [SDWebImageDownloaderOperation class];
  5. //设置执行的规则是FIFO(先进先出)还是LIFO(后进先出)
  6. _executionOrder = SDWebImageDownloaderFIFOExecutionOrder;
  7. //创建Operation队列
  8. _downloadQueue = [NSOperationQueue new];
  9. //设置队列的最大并发数
  10. _downloadQueue.maxConcurrentOperationCount = 6;
  11. }
  12. return self;
  13. }
  1. - (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url
  2. options:(SDWebImageDownloaderOptions)options
  3. progress:(SDWebImageDownloaderProgressBlock)progressBlock
  4. completed:(SDWebImageDownloaderCompletedBlock)completedBlock;

url: 下载的图片的url

options: 一些下载的选项,主要涉及到下载的优先级、缓存、后台任务执行、cookie处理以认证几个方面。

progressBlock: 下载的进度block

completedBlock: 下载完成的block

return: 返回一个遵循<SDWebImageOperation>协议的SDWebImageDownloaderOperation实例对象。

下面就写几个在此函数中跟operation相关的代码:

  1. //创建`SDWebImageDownloaderOperation`实例对象。
  2. operation = [[wself.operationClass alloc] initWithRequest:...];
  3. //是否压缩图片
  4. operation.shouldDecompressImages = wself.shouldDecompressImages;
  5. //下载凭证相关的东西
  6. if (wself.urlCredential) {
  7. operation.credential = wself.urlCredential;
  8. } else if (wself.username && wself.password) {
  9. operation.credential = [NSURLCredential credentialWithUser:wself.username password:wself.password persistence:NSURLCredentialPersistenceForSession];
  10. }
  11. //operation的优先级
  12. if (options & SDWebImageDownloaderHighPriority) {
  13. operation.queuePriority = NSOperationQueuePriorityHigh;
  14. } else if (options & SDWebImageDownloaderLowPriority) {
  15. operation.queuePriority = NSOperationQueuePriorityLow;
  16. }
  17. //加入到队列中
  18. [wself.downloadQueue addOperation:operation];
  19. //判断设置的执行规则,如果是LIFO(后进先出),那就使用addDependency方法,也就是之前最后一个添加的操作(lastAddedOperation)要依赖于最新这个添加进来的操作(operation),也就是要等最新的这个执行完了,之前那个才能执行。
  20. if (wself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) {
  21. // Emulate LIFO execution order by systematically adding new operations as last operation's dependency
  22. [wself.lastAddedOperation addDependency:operation];
  23. wself.lastAddedOperation = operation;
  24. }

内部实现

属性

public

private


方法

里面主要就是一些上面的属性及一些变量的初始化。

  1. - (id)initWithRequest:(NSURLRequest *)request
  2. inSession:(NSURLSession *)session
  3. options:(SDWebImageDownloaderOptions)options
  4. progress:(SDWebImageDownloaderProgressBlock)progressBlock
  5. completed:(SDWebImageDownloaderCompletedBlock)completedBlock
  6. cancelled:(SDWebImageNoParamsBlock)cancelBlock;

这是operation开始相关的操作

  1. - (void)start {
  2. @synchronized (self) {
  3. //判断操作是否取消,如果取消则直接return不执行之后的请求。
  4. if (self.isCancelled) {
  5. self.finished = YES;
  6. [self reset];
  7. return;
  8. }
  9. //创建session
  10. NSURLSession *session = self.unownedSession;
  11. if (!self.unownedSession) {
  12. NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
  13. sessionConfig.timeoutIntervalForRequest = 15;
  14. self.ownedSession = [NSURLSession sessionWithConfiguration:sessionConfig
  15. delegate:self
  16. delegateQueue:nil];
  17. session = self.ownedSession;
  18. }
  19. //创建下载任务
  20. self.dataTask = [session dataTaskWithRequest:self.request];
  21. //标记正在执行
  22. self.executing = YES;
  23. //得到当前的线程
  24. self.thread = [NSThread currentThread];
  25. }
  26. //下载任务开始执行
  27. [self.dataTask resume];
  28. //判断是否有这个下载任务,如果有progressBlock从0开始执行,然后发送开始下载的通知。如果没有则直接completedBlock返回错误。
  29. if (self.dataTask) {
  30. if (self.progressBlock) {
  31. self.progressBlock(0, NSURLResponseUnknownLength);
  32. }
  33. dispatch_async(dispatch_get_main_queue(), ^{
  34. [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStartNotification object:self];
  35. });
  36. }
  37. else {
  38. if (self.completedBlock) {
  39. self.completedBlock(nil, nil, [NSError errorWithDomain:NSURLErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Connection can't be initialized"}], YES);
  40. }
  41. }

这是取消operation相关的操作

  1. - (void)cancel {
  2. @synchronized (self) {
  3. if (self.thread) {
  4. [self performSelector:@selector(cancelInternalAndStop) onThread:self.thread withObject:nil waitUntilDone:NO];
  5. }
  6. else {
  7. [self cancelInternal];
  8. }
  9. }
  10. }
  11. - (void)cancelInternalAndStop {
  12. if (self.isFinished) return;
  13. [self cancelInternal];
  14. }
  15. - (void)cancelInternal {
  16. if (self.isFinished) return;
  17. [super cancel];
  18. if (self.cancelBlock) self.cancelBlock();
  19. if (self.dataTask) {
  20. [self.dataTask cancel];
  21. dispatch_async(dispatch_get_main_queue(), ^{
  22. [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:self];
  23. });
  24. // As we cancelled the connection, its callback won't be called and thus won't
  25. // maintain the isFinished and isExecuting flags.
  26. if (self.isExecuting) self.executing = NO;
  27. if (!self.isFinished) self.finished = YES;
  28. }
  29. [self reset];
  30. }

这是一些重置及设置实例变量的方法

  1. - (void)done {
  2. self.finished = YES;
  3. self.executing = NO;
  4. [self reset];
  5. }
  6. - (void)reset {
  7. self.cancelBlock = nil;
  8. self.completedBlock = nil;
  9. self.progressBlock = nil;
  10. self.dataTask = nil;
  11. self.imageData = nil;
  12. self.thread = nil;
  13. if (self.ownedSession) {
  14. [self.ownedSession invalidateAndCancel];
  15. self.ownedSession = nil;
  16. }
  17. }
  18. - (void)setFinished:(BOOL)finished {
  19. [self willChangeValueForKey:@"isFinished"];
  20. _finished = finished;
  21. [self didChangeValueForKey:@"isFinished"];
  22. }
  23. - (void)setExecuting:(BOOL)executing {
  24. [self willChangeValueForKey:@"isExecuting"];
  25. _executing = executing;
  26. [self didChangeValueForKey:@"isExecuting"];
  27. }
  28. //定义一个并发操作,重写这个方法并且返回YES
  29. - (BOOL)isConcurrent {
  30. return YES;
  31. }

然后就是一些NSURLSessionDataDelegateNSURLSessionTaskDelegate对应的回调方法

问题

为什么要创建SDWebImageDownloaderOperation类对象

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注