YYKit源码分析(10)-YYImage(下)


接下来看图片的下载

YYWebImageManager

这个是网络下载管理类,管理网络下载和缓存

结构


YYKit源码分析(10)-YYImage(下)_第1张图片

单例管理 的类

+ (instancetype)sharedManager {

    static YYWebImageManager *manager;

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

        YYImageCache *cache = [YYImageCache sharedCache];

        NSOperationQueue *queue = [NSOperationQueue new];

        if ([queue respondsToSelector:@selector(setQualityOfService:)]) {

            queue.qualityOfService = NSQualityOfServiceBackground;

        }

        manager = [[self alloc] initWithCache:cache queue:queue];

    });

    return manager;

}

常用手法,不做介绍

这里初始化了磁盘cache。

- (instancetype)initWithCache:(YYImageCache *)cache queue:(NSOperationQueue *)queue{

    self = [super init];

    if (!self) return nil;

    _cache = cache;

    _queue = queue;

    _timeout = 15.0;

    if (YYImageWebPAvailable()) {

        _headers = @{ @"Accept" : @"image/webp,image/*;q=0.8" };

    } else {

        _headers = @{ @"Accept" : @"image/*;q=0.8" };

    }

    return self;

}

这里比较简单不做介绍

- (YYWebImageOperation *)requestImageWithURL:(NSURL *)url

                                    options:(YYWebImageOptions)options

                                    progress:(YYWebImageProgressBlock)progress

                                  transform:(YYWebImageTransformBlock)transform

                                  completion:(YYWebImageCompletionBlock)completion {


    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];

    request.timeoutInterval = _timeout;

    request.HTTPShouldHandleCookies = (options & YYWebImageOptionHandleCookies) != 0;

    request.allHTTPHeaderFields = [self headersForURL:url];

    request.HTTPShouldUsePipelining = YES;

    request.cachePolicy = (options & YYWebImageOptionUseNSURLCache) ?

        NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData;


    YYWebImageOperation *operation = [[YYWebImageOperation alloc] initWithRequest:request

                                                                          options:options

                                                                            cache:_cache

                                                                        cacheKey:[self cacheKeyForURL:url]

                                                                        progress:progress

                                                                        transform:transform ? transform : _sharedTransformBlock

                                                                      completion:completion];

    if (_username && _password) {

        operation.credential = [NSURLCredential credentialWithUser:_username password:_password persistence:NSURLCredentialPersistenceForSession];

    }

    if (operation) {

        NSOperationQueue *queue = _queue;

        if (queue) {

            [queue addOperation:operation];

        } else {

            [operation start];

        }

    }

    return operation;

}

接下来看看网络请求部分

1.获取request

2.获取请求图片的operation

3.在queue中加入到operation 中发起任务。

这里主要是看operation 是如何下载图片的

YYWebImageOperation

具体请求图片就是在operation中执行的,是并且执行。

结构


YYKit源码分析(10)-YYImage(下)_第2张图片

初始化

- (instancetype)initWithRequest:(NSURLRequest *)request

                        options:(YYWebImageOptions)options

                          cache:(YYImageCache *)cache

                      cacheKey:(NSString *)cacheKey

                      progress:(YYWebImageProgressBlock)progress

                      transform:(YYWebImageTransformBlock)transform

                    completion:(YYWebImageCompletionBlock)completion {

    self = [super init];

    if (!self) return nil;

    if (!request) return nil;

    _request = request;

    _options = options;

    _cache = cache;

    _cacheKey = cacheKey ? cacheKey : request.URL.absoluteString;

    _shouldUseCredentialStorage = YES;

    _progress = progress;

    _transform = transform;

    _completion = completion;

    _executing = NO;

    _finished = NO;

    _cancelled = NO;

    _taskID = UIBackgroundTaskInvalid;

    _lock = [NSRecursiveLock new];

    return self;

}

基本初始化。相关变量赋值。没有过多操作

我们知道这个类是NSOpertion的子类,并且是异步的。所以从start 函数启动

- (void)start {

    @autoreleasepool {

        [_lock lock];

        self.started = YES;

        if ([self isCancelled]) {

            [self performSelector:@selector(_cancelOperation) onThread:[[self class] _networkThread] withObject:nil waitUntilDone:NO modes:@[NSDefaultRunLoopMode]];

            self.finished = YES;

        } else if ([self isReady] && ![self isFinished] && ![self isExecuting]) {

            if (!_request) {

                self.finished = YES;

                if (_completion) {

                    NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:@{NSLocalizedDescriptionKey:@"request in nil"}];

                    _completion(nil, _request.URL, YYWebImageFromNone, YYWebImageStageFinished, error);

                }

            } else {

                self.executing = YES;

                [self performSelector:@selector(_startOperation) onThread:[[self class] _networkThread] withObject:nil waitUntilDone:NO modes:@[NSDefaultRunLoopMode]];

                if ((_options & YYWebImageOptionAllowBackgroundTask) && ![UIApplication isAppExtension]) {

                    __weak __typeof__ (self) _self = self;

                    if (_taskID == UIBackgroundTaskInvalid) {

                        _taskID = [[UIApplication sharedExtensionApplication] beginBackgroundTaskWithExpirationHandler:^{

                            __strong __typeof (_self) self = _self;

                            if (self) {

                                [self cancel];

                                self.finished = YES;

                            }

                        }];

                    }

                }

            }

        }

        [_lock unlock];

    }

}

这里使用的锁递归锁

1 标记operation started

2.检查operation 当前状态

3.当operation 取消了, 就执行 -(void)_cancelOperation 操作

《1》执行_cancelOperation 操作

《2》标记 operation 结束

4当operation isReady 

《1》没有reqeust, 标记operation 结束,并且回调completion完成

《2》有request,标记operation executing 。执行_startOperation 

《3》当允许后台执行任务,判断_taskID 是否标记过,没有就标记后台任务完成后就取消operation,并且标记 finish=yes

这里我们看看_networkThread

/// Global image request network thread, used by NSURLConnection delegate.

+ (NSThread *)_networkThread {

    static NSThread *thread = nil;

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

        thread = [[NSThread alloc] initWithTarget:self selector:@selector(_networkThreadMain:) object:nil];

        if ([thread respondsToSelector:@selector(setQualityOfService:)]) {

            thread.qualityOfService = NSQualityOfServiceBackground;

        }

        [thread start];

    });

    return thread;

}

单例,生成一条NSTread,网络线程,并且开始runloop。

- (void)_startOperation {

    if ([self isCancelled]) return;

    @autoreleasepool {

        // get image from cache

        if (_cache &&

            !(_options & YYWebImageOptionUseNSURLCache) &&

            !(_options & YYWebImageOptionRefreshImageCache)) {

            UIImage *image = [_cache getImageForKey:_cacheKey withType:YYImageCacheTypeMemory];

            if (image) {

                [_lock lock];

                if (![self isCancelled]) {

                    if (_completion) _completion(image, _request.URL, YYWebImageFromMemoryCache, YYWebImageStageFinished, nil);

                }

                [self _finish];

                [_lock unlock];

                return;

            }

            if (!(_options & YYWebImageOptionIgnoreDiskCache)) {

                __weak typeof(self) _self = self;

                dispatch_async([self.class _imageQueue], ^{

                    __strong typeof(_self) self = _self;

                    if (!self || [self isCancelled]) return;

                    UIImage *image = [self.cache getImageForKey:self.cacheKey withType:YYImageCacheTypeDisk];

                    if (image) {

                        [self.cache setImage:image imageData:nil forKey:self.cacheKey withType:YYImageCacheTypeMemory];

                        [self performSelector:@selector(_didReceiveImageFromDiskCache:) onThread:[self.class _networkThread] withObject:image waitUntilDone:NO];

                    } else {

                        [self performSelector:@selector(_startRequest:) onThread:[self.class _networkThread] withObject:nil waitUntilDone:NO];

                    }

                });

                return;

            }

        }

    }

    [self performSelector:@selector(_startRequest:) onThread:[self.class _networkThread] withObject:nil waitUntilDone:NO];

}


这个是在线程名字是com.ibireme.yykit.webimage.request执行的

1.检查operation是否取消

2.从缓存中查找,因为这里是在线程中,所以用的同步查询

《1》先从内存中查找,找到就返回

《2》再从磁盘上查找,找到就返回,在磁盘找的时候是异步的。

《3》没有执行网络请求

3.执行网络请求

// runs on network thread

- (void)_startRequest:(id)object {

    if ([self isCancelled]) return;

    @autoreleasepool {

        if ((_options & YYWebImageOptionIgnoreFailedURL) && URLBlackListContains(_request.URL)) {

            NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:@{ NSLocalizedDescriptionKey : @"Failed to load URL, blacklisted." }];

            [_lock lock];

            if (![self isCancelled]) {

                if (_completion) _completion(nil, _request.URL, YYWebImageFromNone, YYWebImageStageFinished, error);

            }

            [self _finish];

            [_lock unlock];

            return;

        }


        if (_request.URL.isFileURL) {

            NSArray *keys = @[NSURLFileSizeKey];

            NSDictionary *attr = [_request.URL resourceValuesForKeys:keys error:nil];

            NSNumber *fileSize = attr[NSURLFileSizeKey];

            _expectedSize = (fileSize != nil) ? fileSize.unsignedIntegerValue : -1;

        }


        // request image from web

        [_lock lock];

        if (![self isCancelled]) {

            _connection = [[NSURLConnection alloc] initWithRequest:_request delegate:[YYWeakProxy proxyWithTarget:self]];

            if (![_request.URL isFileURL] && (_options & YYWebImageOptionShowNetworkActivity)) {

                [[UIApplication sharedExtensionApplication] incrementNetworkActivityCount];

            }

        }

        [_lock unlock];

    }

}

执行网络请求

1 这里检测operation是否被取消了

2.url是否再黑名单中,在黑名单就返回error

3.用NSURLConnection 连接

4.打开网络请求指示器

接下来看看网络请求的回调函数

网络请求的回调是SDWebimage一样的处理方式,不做介绍了

我认为这个类还是做个流程图看看吧


YYKit源码分析(10)-YYImage(下)_第3张图片

大概流程就是这个样子的。networkThread 是管理网络下载或者查询数据的

而imageQueue是管理磁盘或者生成image 并且保存image到cache中的

其他的就不多说了。

类别有啥呢

UIImageView+YYWebImage

UIButton+YYWebImage

CALayer+YYWebImage

MKAnnotationView+YYWebImage

这里还有个特殊类

_YYWebImageSetter

先看这个特殊类

属性有两个

/// Current image url.

@property (nullable, nonatomic, readonly) NSURL *imageURL;

/// Current sentinel.

@property (nonatomic, readonly) int32_t sentinel;

私有变量有四个

dispatch_semaphore_t _lock;

    NSURL *_imageURL;

    NSOperation *_operation;

    int32_t _sentinel;

_sentinel 记录当前sentinel,当做一个标记吧

这里主要的函数是

- (int32_t)setOperationWithSentinel:(int32_t)sentinel

                                url:(NSURL *)imageURL

                            options:(YYWebImageOptions)options

                            manager:(YYWebImageManager *)manager

                          progress:(YYWebImageProgressBlock)progress

                          transform:(YYWebImageTransformBlock)transform

                        completion:(YYWebImageCompletionBlock)completion {

    if (sentinel != _sentinel) {

        if (completion) completion(nil, imageURL, YYWebImageFromNone, YYWebImageStageCancelled, nil);

        return _sentinel;

    }


    NSOperation *operation = [manager requestImageWithURL:imageURL options:options progress:progress transform:transform completion:completion];

    if (!operation && completion) {

        NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : @"YYWebImageOperation create failed." };

        completion(nil, imageURL, YYWebImageFromNone, YYWebImageStageFinished, [NSError errorWithDomain:@"com.ibireme.yykit.webimage" code:-1 userInfo:userInfo]);

    }


    dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER);

    if (sentinel == _sentinel) {

        if (_operation) [_operation cancel];

        _operation = operation;

        sentinel = OSAtomicIncrement32(&_sentinel);

    } else {

        [operation cancel];

    }

    dispatch_semaphore_signal(_lock);

    return sentinel;

}

1 。要是sentinel 改变了,那么久说明取消操作,调用完成block,返回上一次请求的_sentienl

2.sentinel 没变化,获取operation 

《1 》要是operation 有,说明在下载,

《2》要是没有operation ,那么说明没有下载图片,那么就调用完成block

3.要是sentinel 不变,那么取消上一个的operation,将sentinel 增加

4.要是sentinel变化了。当前的operation 也取消掉

你可能感兴趣的:(YYKit源码分析(10)-YYImage(下))