美文网首页
八、SDWebImage源码解析之SDWebImageDownl

八、SDWebImage源码解析之SDWebImageDownl

作者: 小强简书 | 来源:发表于2018-02-08 15:54 被阅读40次

    SDWebImageDownloaderOperation继承自NSOperation,是具体的执行图片下载的单位。负责生成NSURLSessionTask进行图片请求,支持下载取消和后台下载,在下载中及时汇报下载进度,在下载成功后,对图片进行解码,缩放和压缩等操作

    一些通知

    FOUNDATION_EXPORT NSString * _Nonnull const SDWebImageDownloadStartNotification;//开始下载通知
    FOUNDATION_EXPORT NSString * _Nonnull const SDWebImageDownloadReceiveResponseNotification;//收到response通知
    FOUNDATION_EXPORT NSString * _Nonnull const SDWebImageDownloadStopNotification;//停止下载通知
    FOUNDATION_EXPORT NSString * _Nonnull const SDWebImageDownloadFinishNotification;//结束下载通知
    
    

    SDWebImageDownloaderOperationInterface deleagte方法

    @protocol SDWebImageDownloaderOperationInterface<NSObject>
    
    - (nonnull instancetype)initWithRequest:(nullable NSURLRequest *)request
                                  inSession:(nullable NSURLSession *)session
                                    options:(SDWebImageDownloaderOptions)options;
    //绑定DownloaderOperation的下载进度block和结束block
    - (nullable id)addHandlersForProgress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                                completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock;
    //返回当前下载操作的图片是否应该压缩
    - (BOOL)shouldDecompressImages;
    - (void)setShouldDecompressImages:(BOOL)value;//设置是否应该压缩图片
    
    - (nullable NSURLCredential *)credential;//返回身份认证
    - (void)setCredential:(nullable NSURLCredential *)value;//设置身份认证
    
    @end
    
    

    SDWebImageDownloaderOperation.h

    
    @property (assign, nonatomic, readonly) SDWebImageDownloaderOptions options;
    
    /**
     * The expected size of data.
     *///期望data的大小size
    @property (assign, nonatomic) NSInteger expectedSize;
    
    /**
     * The response returned by the operation's connection.
     *///operation的response
    @property (strong, nonatomic, nullable) NSURLResponse *response;
    
    /**
     *  Initializes a `SDWebImageDownloaderOperation` object
     *
     *  @see SDWebImageDownloaderOperation
     *
     *  @param request        the URL request
     *  @param session        the URL session in which this operation will run
     *  @param options        downloader options
     *
     *  @return the initialized instance
     *///初始化一个SDWebImageDownloaderOperation对象
    - (nonnull instancetype)initWithRequest:(nullable NSURLRequest *)request
                                  inSession:(nullable NSURLSession *)session
                                    options:(SDWebImageDownloaderOptions)options NS_DESIGNATED_INITIALIZER;
    
    /**
     *  Adds handlers for progress and completion. Returns a tokent that can be passed to -cancel: to cancel this set of
     *  callbacks.
     *
     *  @param progressBlock  the block executed when a new chunk of data arrives.
     *                        @note the progress block is executed on a background queue
     *  @param completedBlock the block executed when the download is done.
     *                        @note the completed block is executed on the main queue for success. If errors are found, there is a chance the block will be executed on a background queue
     *
     *  @return the token to use to cancel this set of handlers
     *///绑定DownloaderOperation的下载进度block和结束block
    - (nullable id)addHandlersForProgress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                                completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock;
    
    /**
     *  Cancels a set of callbacks. Once all callbacks are canceled, the operation is cancelled.
     *
     *  @param token the token representing a set of callbacks to cancel
     *
     *  @return YES if the operation was stopped because this was the last token to be canceled. NO otherwise.
     *///SDWebImageOperation的协议方法
    - (BOOL)cancel:(nullable id)token;
    
    @end
    
    //静态全局变量作为下载进度block字典存储的key
    static NSString *const kProgressCallbackKey = @"progress";
    //静态全局变量作为结束下载block字典存储的key
    static NSString *const kCompletedCallbackKey = @"completed";
    

    SDWebImageDownloaderOperation属性

    @interface SDWebImageDownloaderOperation ()
    
    @property (strong, nonatomic, nonnull) NSMutableArray<SDCallbacksDictionary *> *callbackBlocks;
    
    @property (assign, nonatomic, getter = isExecuting) BOOL executing;//是否正在执行
    @property (assign, nonatomic, getter = isFinished) BOOL finished;//是否下载结束
    @property (strong, nonatomic, nullable) NSMutableData *imageData;
    @property (copy, nonatomic, nullable) NSData *cachedData;//缓存的data
    
    // This is weak because it is injected by whoever manages this session. If this gets nil-ed out, we won't be able to run
    // the task associated with this operation
    //网络请求session,注意这里使用的属性修饰符是weak,因为unownedSession是从SDWebImageDownloader传过来的,所以需要SDWebImageDownloader管理
    @property (weak, nonatomic, nullable) NSURLSession *unownedSession;
    // This is set if we're using not using an injected NSURLSession. We're responsible of invalidating this one
    //网络请求session,注意这里使用的属性修饰符是strong,如果unownedSession是nil,我们需要手动创建一个并且管理它的生命周期和代理方法
    @property (strong, nonatomic, nullable) NSURLSession *ownedSession;
    //网络请求具体的task
    @property (strong, nonatomic, readwrite, nullable) NSURLSessionTask *dataTask;
    //全局并行队列,控制数据
    @property (strong, nonatomic, nullable) dispatch_queue_t barrierQueue;
    
    #if SD_UIKIT
    @property (assign, nonatomic) UIBackgroundTaskIdentifier backgroundTaskId;  //如果用户设置了后台继续加载选项,通过backgroundTask来继续下载图片
    #endif
    
    @property (strong, nonatomic, nullable) id<SDWebImageProgressiveCoder> progressiveCoder;//图片解码器,有意思的是如果图片没有完全下载完成时也可以解码展示部分图片  ******
    
    @end
    
    

    初始化方法

    - (nonnull instancetype)init {
        return [self initWithRequest:nil inSession:nil options:0];
    }
    //初始化方法,进行一些基础数据的赋值配置
    - (nonnull instancetype)initWithRequest:(nullable NSURLRequest *)request
                                  inSession:(nullable NSURLSession *)session
                                    options:(SDWebImageDownloaderOptions)options {
        if ((self = [super init])) {
            _request = [request copy];
            _shouldDecompressImages = YES;
            _options = options;
            _callbackBlocks = [NSMutableArray new];
            _executing = NO;
            _finished = NO;
            _expectedSize = 0;
            _unownedSession = session;
            _barrierQueue = dispatch_queue_create("com.hackemist.SDWebImageDownloaderOperationBarrierQueue", DISPATCH_QUEUE_CONCURRENT);
        }
        return self;
    }
    

    绑定DownloaderOperation的下载进度block和结束block

    - (nullable id)addHandlersForProgress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                                completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock {
        SDCallbacksDictionary *callbacks = [NSMutableDictionary new];
        if (progressBlock) callbacks[kProgressCallbackKey] = [progressBlock copy];
        if (completedBlock) callbacks[kCompletedCallbackKey] = [completedBlock copy];
        dispatch_barrier_async(self.barrierQueue, ^{ //添加一个执行一个,执行完事,再添加另一个
            [self.callbackBlocks addObject:callbacks];
        });
        return callbacks;
    }
    

    根据key获取回调块数组中所有对应key的回调块

    - (nullable NSArray<id> *)callbacksForKey:(NSString *)key {
        __block NSMutableArray<id> *callbacks = nil;
        dispatch_sync(self.barrierQueue, ^{
            // We need to remove [NSNull null] because there might not always be a progress block for each callback
            callbacks = [[self.callbackBlocks valueForKey:key] mutableCopy];
            [callbacks removeObjectIdenticalTo:[NSNull null]];
        });
        return [callbacks copy];    // strip mutability here
    }
    

    取消方法

    - (BOOL)cancel:(nullable id)token {
        __block BOOL shouldCancel = NO;
        dispatch_barrier_sync(self.barrierQueue, ^{
            [self.callbackBlocks removeObjectIdenticalTo:token]; //删除指定的对象,根据对象的地址来判断
            if (self.callbackBlocks.count == 0) {
                shouldCancel = YES;
            }
        });
        if (shouldCancel) {
            [self cancel];
        }
        return shouldCancel;
    }
    

    重写operation的start方法,当任务添加到NSOperationQueue后会执行该方法,启动下载任务

    - (void)start {
        //添加同步锁,防止多线程数据竞争
        @synchronized (self) {
            if (self.isCancelled) {
                self.finished = YES;
                [self reset];
                return;
            }
    
    //1
    #if SD_UIKIT
            //如调用者配置了在后台可以继续下载图片,那么在这里继续下载
            Class UIApplicationClass = NSClassFromString(@"UIApplication");
            BOOL hasApplication = UIApplicationClass && [UIApplicationClass respondsToSelector:@selector(sharedApplication)];
            if (hasApplication && [self shouldContinueWhenAppEntersBackground]) {
                __weak __typeof__ (self) wself = self;
                UIApplication * app = [UIApplicationClass performSelector:@selector(sharedApplication)];
                self.backgroundTaskId = [app beginBackgroundTaskWithExpirationHandler:^{
                    __strong __typeof (wself) sself = wself;
    
                    if (sself) {
                        [sself cancel];
    
                        [app endBackgroundTask:sself.backgroundTaskId];
                        sself.backgroundTaskId = UIBackgroundTaskInvalid;
                    }
                }];
            }
    #endif
            
            //获取网络请求的缓存数据
            if (self.options & SDWebImageDownloaderIgnoreCachedResponse) {
                // Grab the cached data for later check
                NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:self.request];
                if (cachedResponse) {
                    self.cachedData = cachedResponse.data;
                }
            }
            
            NSURLSession *session = self.unownedSession;
            //判断unownedSession是否为了nil,如果是nil则重新创建一个ownedSession
            if (!self.unownedSession) {
                NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
                sessionConfig.timeoutIntervalForRequest = 15;            //超时时间15s
                
                /**
                 *  Create the session for this task
                 *  We send nil as delegate queue so that the session creates a serial operation queue for performing all delegate
                 *  method calls and completion handler calls.
                 */
                //delegateQueue为nil,所以回调方法默认在一个子线程的串行队列中执行
                self.ownedSession = [NSURLSession sessionWithConfiguration:sessionConfig
                                                                  delegate:self
                                                             delegateQueue:nil];
                session = self.ownedSession;
            }
            //使用session来创建一个NSURLSessionDataTask类型下载任务
            self.dataTask = [session dataTaskWithRequest:self.request];
            self.executing = YES;
        }
        //开始执行任务
        [self.dataTask resume];
    
        if (self.dataTask) {
            //任务开启,遍历进度块数组 执行第一个下载进度 进度为0
            for (SDWebImageDownloaderProgressBlock progressBlock in [self callbacksForKey:kProgressCallbackKey]) {
                progressBlock(0, NSURLResponseUnknownLength, self.request.URL);
            }
            __weak typeof(self) weakSelf = self;
            dispatch_async(dispatch_get_main_queue(), ^{
                //在主线程发送通知,并将self传出去,为什么在主线程发送呢?
                //因为在哪个线程发送通知就在哪个线程接受通知,这样如果在接受通知方法中执行修改UI的操作也不会有问题
                [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStartNotification object:weakSelf];
            });
        } else {
            //如果创建tataTask失败就执行失败的回调块
            [self callCompletionBlocksWithError:[NSError errorWithDomain:NSURLErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Connection can't be initialized"}]];
        }
    
    //2     1和2中间插入需要长时间运行的代码
    #if SD_UIKIT
        //后台继续下载
        Class UIApplicationClass = NSClassFromString(@"UIApplication");
        if(!UIApplicationClass || ![UIApplicationClass respondsToSelector:@selector(sharedApplication)]) {
            return;
        }
        if (self.backgroundTaskId != UIBackgroundTaskInvalid) {
            UIApplication * app = [UIApplication performSelector:@selector(sharedApplication)];
            [app endBackgroundTask:self.backgroundTaskId];
            self.backgroundTaskId = UIBackgroundTaskInvalid;
        }
    #endif
    }
    

    SDWebImageOperation的协议方法,取消方法

    - (void)cancel {
        @synchronized (self) {
            [self cancelInternal];
        }
    }
    

    取消下载

    - (void)cancelInternal {
        if (self.isFinished) return;
        [super cancel];
        //如果下载图片的任务仍在 则立即取消cancel,并且发送结束下载的通知
        if (self.dataTask) {
            [self.dataTask cancel];
            __weak typeof(self) weakSelf = self;
            dispatch_async(dispatch_get_main_queue(), ^{
                [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:weakSelf];
            });
    
            // 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];
    }
    

    下载完成后调用的方法,重置finished和executing属性

    - (void)done {
        self.finished = YES;
        self.executing = NO;
        [self reset];
    }
    
    //重新设置数据的方法
    - (void)reset {
        __weak typeof(self) weakSelf = self;
        //删除回调块字典数组的所有元素
        dispatch_barrier_async(self.barrierQueue, ^{
            [weakSelf.callbackBlocks removeAllObjects];
        });
        self.dataTask = nil;
        
        NSOperationQueue *delegateQueue;
        //如果unownedSession存在就从它里面获取delegateQueue,否则从ownedSession获取
        if (self.unownedSession) {
            delegateQueue = self.unownedSession.delegateQueue;
        } else {
            delegateQueue = self.ownedSession.delegateQueue;
        }
        //如果存在代理方法执行队列
        if (delegateQueue) {
            //delegateQueue必须是串行队列 最大并发量为1
            NSAssert(delegateQueue.maxConcurrentOperationCount == 1, @"NSURLSession delegate queue should be a serial queue");
            [delegateQueue addOperationWithBlock:^{
                weakSelf.imageData = nil;
            }];
        }
        //如果ownedSession存在,则手动调用invalidateAndCancel进行任务
        if (self.ownedSession) {
            [self.ownedSession invalidateAndCancel];
            self.ownedSession = nil;
        }
    }
    

    finished属性的setter

    - (void)setFinished:(BOOL)finished {
        [self willChangeValueForKey:@"isFinished"];//手动触发KVO通知
        _finished = finished;
        [self didChangeValueForKey:@"isFinished"];
    }
    

    executing属性的setter

    - (void)setExecuting:(BOOL)executing {
        [self willChangeValueForKey:@"isExecuting"];//手动触发KVO通知
        _executing = executing;
        [self didChangeValueForKey:@"isExecuting"];
    }
    

    重写NSOperation方法,标识这是一个并发任务

    - (BOOL)isConcurrent {
        return YES;
    }
    

    NSURLSessionDataDelegate,NSURLSessionTaskDelegate方法

    //收到服务端响应,在一次请求中只会执行一次
    - (void)URLSession:(NSURLSession *)session
              dataTask:(NSURLSessionDataTask *)dataTask
    didReceiveResponse:(NSURLResponse *)response
     completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
        //根据状态码来判断请求的状态
        //'304 Not Modified' is an exceptional one
        if (![response respondsToSelector:@selector(statusCode)] || (((NSHTTPURLResponse *)response).statusCode < 400 && ((NSHTTPURLResponse *)response).statusCode != 304)) {
            //获取要下载图片的长度
            NSInteger expected = (NSInteger)response.expectedContentLength;
            expected = expected > 0 ? expected : 0;
             //设置当前expectedSize
            self.expectedSize = expected;
            //遍历进度回调块并触发进度回调块
            for (SDWebImageDownloaderProgressBlock progressBlock in [self callbacksForKey:kProgressCallbackKey]) {
                progressBlock(0, expected, self.request.URL);
            }
            //根据response返回的文件大小创建可变data
            self.imageData = [[NSMutableData alloc] initWithCapacity:expected];
            self.response = response;
            __weak typeof(self) weakSelf = self;
            dispatch_async(dispatch_get_main_queue(), ^{
                //在主线程发送接受response的通知
                [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadReceiveResponseNotification object:weakSelf];
            });
        } else {        //进入失败的情况
    
            NSUInteger code = ((NSHTTPURLResponse *)response).statusCode;
            
            //This is the case when server returns '304 Not Modified'. It means that remote image is not changed.
            //In case of 304 we need just cancel the operation and return cached image from the cache.
            //如果code等于304直接取消下载,否则执行URLSession:task:didCompleteWithError:代理方法
            if (code == 304) {
                [self cancelInternal];
            } else {
                [self.dataTask cancel];
            }
            __weak typeof(self) weakSelf = self;
            dispatch_async(dispatch_get_main_queue(), ^{
                ///主线程发送结束下载的通知
                [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:weakSelf];
            });
            //执行下载失败异常的代码块
            [self callCompletionBlocksWithError:[NSError errorWithDomain:NSURLErrorDomain code:((NSHTTPURLResponse *)response).statusCode userInfo:nil]];
            //执行一些结束下载的操作
            [self done];
        }
        
        if (completionHandler) {
            completionHandler(NSURLSessionResponseAllow);
        }
    }
    
    //每次收到数据都会触发 可能是多次
    - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
        [self.imageData appendData:data];  //向可变数据中添加接收到的数据
        //如果调用者配置了需要支持progressive下载,即展示已经下载的部分,并expectedSize返回的图片size大于0
        if ((self.options & SDWebImageDownloaderProgressiveDownload) && self.expectedSize > 0) {
            // Get the image data
            NSData *imageData = [self.imageData copy];
            // Get the total bytes downloaded
            const NSInteger totalSize = imageData.length;
            // Get the finish status
            BOOL finished = (totalSize >= self.expectedSize);
            
            if (!self.progressiveCoder) {
                // We need to create a new instance for progressive decoding to avoid conflicts
                for (id<SDWebImageCoder>coder in [SDWebImageCodersManager sharedInstance].coders) {
                    if ([coder conformsToProtocol:@protocol(SDWebImageProgressiveCoder)] &&
                        [((id<SDWebImageProgressiveCoder>)coder) canIncrementallyDecodeFromData:imageData]) {
                        self.progressiveCoder = [[[coder class] alloc] init];
                        break;
                    }
                }
            }
            
            UIImage *image = [self.progressiveCoder incrementallyDecodedImageWithData:imageData finished:finished];
            if (image) {
                NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:self.request.URL];
                image = [self scaledImageForKey:key image:image];
                if (self.shouldDecompressImages) {
                    image = [[SDWebImageCodersManager sharedInstance] decompressedImageWithImage:image data:&data options:@{SDWebImageCoderScaleDownLargeImagesKey: @(NO)}];
                }
                
                [self callCompletionBlocksWithImage:image imageData:nil error:nil finished:NO];
            }
        }
    
        for (SDWebImageDownloaderProgressBlock progressBlock in [self callbacksForKey:kProgressCallbackKey]) {
            progressBlock(self.imageData.length, self.expectedSize, self.request.URL);
        }
    }
    
    //如果需要缓存response则做一些处理
    - (void)URLSession:(NSURLSession *)session
              dataTask:(NSURLSessionDataTask *)dataTask
     willCacheResponse:(NSCachedURLResponse *)proposedResponse
     completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler {
        
        NSCachedURLResponse *cachedResponse = proposedResponse;
    
        if (self.request.cachePolicy == NSURLRequestReloadIgnoringLocalCacheData) {
            // Prevents caching of responses
            cachedResponse = nil;
        }
        if (completionHandler) {
            completionHandler(cachedResponse);
        }
    }
    
    #pragma mark NSURLSessionTaskDelegate
    //下载完成或下载失败时的回调方法
    - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
        @synchronized(self) {
            self.dataTask = nil;
            __weak typeof(self) weakSelf = self;
            dispatch_async(dispatch_get_main_queue(), ^{
                [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:weakSelf];
                if (!error) {
                    [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadFinishNotification object:weakSelf];
                }
            });
        }
        
        if (error) {
            [self callCompletionBlocksWithError:error];
        } else {
            if ([self callbacksForKey:kCompletedCallbackKey].count > 0) {
                /**
                 *  If you specified to use `NSURLCache`, then the response you get here is what you need.
                 */
                NSData *imageData = [self.imageData copy];
                if (imageData) {
                    /**  if you specified to only use cached data via `SDWebImageDownloaderIgnoreCachedResponse`,
                     *  then we should check if the cached data is equal to image data
                     */
                    if (self.options & SDWebImageDownloaderIgnoreCachedResponse && [self.cachedData isEqualToData:imageData]) {
                        // call completion block with nil
                        [self callCompletionBlocksWithImage:nil imageData:nil error:nil finished:YES];
                    } else {
                        UIImage *image = [[SDWebImageCodersManager sharedInstance] decodedImageWithData:imageData];
                        NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:self.request.URL];
                        image = [self scaledImageForKey:key image:image];
                        
                        BOOL shouldDecode = YES;
                        // Do not force decoding animated GIFs and WebPs
                        if (image.images) {
                            shouldDecode = NO;
                        } else {
    #ifdef SD_WEBP
                            SDImageFormat imageFormat = [NSData sd_imageFormatForImageData:imageData];
                            if (imageFormat == SDImageFormatWebP) {
                                shouldDecode = NO;
                            }
    #endif
                        }
                        
                        if (shouldDecode) {
                            if (self.shouldDecompressImages) {
                                BOOL shouldScaleDown = self.options & SDWebImageDownloaderScaleDownLargeImages;
                                image = [[SDWebImageCodersManager sharedInstance] decompressedImageWithImage:image data:&imageData options:@{SDWebImageCoderScaleDownLargeImagesKey: @(shouldScaleDown)}];
                            }
                        }
                        CGSize imageSize = image.size;
                        if (imageSize.width == 0 || imageSize.height == 0) {
                            [self callCompletionBlocksWithError:[NSError errorWithDomain:SDWebImageErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Downloaded image has 0 pixels"}]];
                        } else {
                            [self callCompletionBlocksWithImage:image imageData:imageData error:nil finished:YES];
                        }
                    }
                } else {
                    [self callCompletionBlocksWithError:[NSError errorWithDomain:SDWebImageErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Image data is nil"}]];
                }
            }
        }
        [self done];
    }
    
    //针对服务器返回的证书进行处理, 需要在该方法中告诉系统是否需要安装服务器返回的证书
    - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {
        
        NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
        __block NSURLCredential *credential = nil;
        
        if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
            if (!(self.options & SDWebImageDownloaderAllowInvalidSSLCertificates)) {
                disposition = NSURLSessionAuthChallengePerformDefaultHandling;
            } else {
                credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
                disposition = NSURLSessionAuthChallengeUseCredential;
            }
        } else {
            if (challenge.previousFailureCount == 0) {
                if (self.credential) {
                    credential = self.credential;
                    disposition = NSURLSessionAuthChallengeUseCredential;
                } else {
                    disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
                }
            } else {
                disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
            }
        }
        
        if (completionHandler) {
            completionHandler(disposition, credential);
        }
    }
    

    相关文章

      网友评论

          本文标题:八、SDWebImage源码解析之SDWebImageDownl

          本文链接:https://www.haomeiwen.com/subject/bopttftx.html