YYWebImage 源码解析

YYWebImage 源码解析

项目地址:YYWebImage,分析的版本:b9f0a2a

1. 功能介绍

YYWebImage 是一个异步图片加载框架 (YYKit 组件之一).

其设计目的是试图替代 SDWebImage、PINRemoteImage、FLAnimatedImage 等开源框架,它支持这些开源框架的大部分功能,同时增加了大量新特性、并且有不小的性能提升。

它底层用 YYCache 实现了内存和磁盘缓存, 用 YYImage 实现了 WebP/APNG/GIF 动图的解码和播放。

2. 流程图

UIImageVie

3. 详细设计

3.2 项目结构


Cache 文件夹里面装的就是 YYCache,具体的源码解析我在之前的文章里面已经介绍过了,这里就不具体说了,只要记得这是一个跟缓存有关的。

Image 文件夹里面装的是 YYImage,这是 ibireme 开源的一个 iOS 图像框架,我们现在不用做具体了解,只要记得这是跟图像解码编码相关的。

YYWebImage.h 就是引入头文件的作用,没啥好说的。

YYImageCache 这个看名字就能知道它负责了缓存相关的功能。

YYWebImageOperation 是 NSOperation 的子类实现,它会根据 URL 进行请求来获取图片,主要干活的也就是它。

YYWebImageManager 它就是负责来创建和管理上面的 YYWebImageOperation。

_YYWebImageSetter 的设计是作为私有持有的,所以不会出现在 .h 文件当中,最后其实也是由它来调用 YYWebImageManager。

CALayer+YYWebImageMKAnnotationView+YYWebImageUIButton+YYWebImageUIImageView+YYWebImage 的内容大体相近,下面就不每个都展开来说,会挑一个来说一下具体内容。它们的作用就是给UIImageView 等提供图片网络请求的功能了。

3.3 详细介绍

开始分析之前我先大概的描述下流程是怎样的,下面会用一些伪代码来讲解。

比如我们有一个叫 girlImageView 的 ImageView,我们可以使用 UIImageView+YYWebImage 拓展出来的 yy_setImageWithURL:placeholder: 方法来进行图片的设置。

NSURL *url = [NSURL URLWithString:@"http://img.hb.aicdn.com/18ee8e5d99e0dc008f040a40092a4ba1c73f1902e208-RAVWip_fw658"];
[girlImageView yy_setImageWithURL:url placeholder:[UIImage imageNamed:@"placeholder"]];

上面方法调用之后就能显示出美女图片,那么这个图片是如何下载显示的呢,我们下面来看下相关的伪代码

- (void)yy_setImageWithURL:(NSURL *)imageURL placeholder:(UIImage *)placeholder {
    YYWebImageManager *manager = //如果没自定义的 manager,就返回默认的。
    _YYWebImageSetter *setter = //通过 objc_getAssociatedObject 获取到 setter

    // 在 UI 线程运行
    _yy_dispatch_sync_on_main_queue(^{
            //1.
            [manager 看下有没内存缓存,有的话就搞定了];
            //2.
            设置 placeholder;
            // 在自定义的非主队列上面跑
            dispatch_async([_YYWebImageSetter setterQueue], ^{
                //3.
                setter 拿着 manager  imageURL 生成 operation 进行网络请求;
                //4. 在 UI 线程运行
                网络请求成功的回调将 image 设置到 imageview
            });

    });
}

3.3.1 YYImageCache

// 缓存名字
@property (nullable, copy) NSString *name;
// YYCache 中的内存缓存
@property (strong, readonly) YYMemoryCache *memoryCache;
// YYCache 中的磁盘缓存
@property (strong, readonly) YYDiskCache *diskCache;
// 从磁盘缓存中读取到动图的时候是否播放动图
@property BOOL allowAnimatedImage;
// 是否启用解码显示,其实就是用到 YYImageCoder 的拓展方法
@property BOOL decodeForDisplay;

// 计算 image 的像素总和

- (NSUInteger)imageCost:(UIImage *)image {}
// data 转 image

- (UIImage *)imageFromData:(NSData *)data {}

// 单例,初始化缓存路径
+ (instancetype)sharedCache {}

// 初始化内存缓存和磁盘缓存

- (instancetype)initWithPath:(NSString *)path {
    YYMemoryCache *memoryCache = [YYMemoryCache new];
    memoryCache.shouldRemoveAllObjectsOnMemoryWarning = YES;
    memoryCache.shouldRemoveAllObjectsWhenEnteringBackground = YES;
    memoryCache.countLimit = NSUIntegerMax;
    memoryCache.costLimit = NSUIntegerMax;
    memoryCache.ageLimit = 12 * 60 * 60;

    YYDiskCache *diskCache = [[YYDiskCache alloc] initWithPath:path];
    diskCache.customArchiveBlock = ^(id object) { return (NSData *)object; };
    diskCache.customUnarchiveBlock = ^(NSData *data) { return (id)data; };
    if (!memoryCache || !diskCache) return nil;

    self = [super init];
    _memoryCache = memoryCache;
    _diskCache = diskCache;
    _allowAnimatedImage = YES;
    _decodeForDisplay = YES;
    return self;
}


- (void)setImage:(UIImage *)image forKey:(NSString *)key {
    [self setImage:image imageData:nil forKey:key withType:YYImageCacheTypeAll];
}

// 设置图片缓存,像上面的调用就是默认使用 All
// 如果是支持内存缓存就内存缓存,支持磁盘缓存就磁盘缓存

- (void)setImage:(UIImage *)image imageData:(NSData *)imageData forKey:(NSString *)key withType:(YYImageCacheType)type {
    if (!key || (image == nil && imageData.length == 0)) return;

    __weak typeof(self) _self = self;
    if (type & YYImageCacheTypeMemory) { // add to memory cache
        if (image) {
            if (image.yy_isDecodedForDisplay) {// 怀疑这里是不是少写了!
                [_memoryCache setObject:image forKey:key withCost:[_self imageCost:image]];
            } else {
                dispatch_async(YYImageCacheDecodeQueue(), ^{
                    __strong typeof(_self) self = _self;
                    if (!self) return;
                    [self.memoryCache setObject:[image yy_imageByDecoded] forKey:key withCost:[self imageCost:image]];
                });
            }
        } else if (imageData) {
            dispatch_async(YYImageCacheDecodeQueue(), ^{
                __strong typeof(_self) self = _self;
                if (!self) return;
                UIImage *newImage = [self imageFromData:imageData];
                [self.memoryCache setObject:newImage forKey:key withCost:[self imageCost:newImage]];
            });
        }
    }
    if (type & YYImageCacheTypeDisk) { // add to disk cache
        if (imageData) {
            if (image) {
                [YYDiskCache setExtendedData:[NSKeyedArchiver archivedDataWithRootObject:@(image.scale)] toObject:imageData];
            }
            [_diskCache setObject:imageData forKey:key];
        } else if (image) {
            dispatch_async(YYImageCacheIOQueue(), ^{
                __strong typeof(_self) self = _self;
                if (!self) return;
                NSData *data = [image yy_imageDataRepresentation];
                [YYDiskCache setExtendedData:[NSKeyedArchiver archivedDataWithRootObject:@(image.scale)] toObject:data];
                [self.diskCache setObject:data forKey:key];
            });
        }
    }
}

// 下面的移除、包含和获取实际上都是对内存缓存和磁盘缓存的操作
// 主要就是对内存缓存进行操作再磁盘缓存操作

- (void)removeImageForKey:(NSString *)key {}
- (void)removeImageForKey:(NSString *)key withType:(YYImageCacheType)type {}
- (BOOL)containsImageForKey:(NSString *)key {}
- (BOOL)containsImageForKey:(NSString *)key withType:(YYImageCacheType)type {}
- (UIImage *)getImageForKey:(NSString *)key {}
- (UIImage *)getImageForKey:(NSString *)key withType:(YYImageCacheType)type {}
- (void)getImageForKey:(NSString *)key withType:(YYImageCacheType)type withBlock:(void (^)(UIImage *image, YYImageCacheType type))block {}
- (NSData *)getImageDataForKey:(NSString *)key {}
- (void)getImageDataForKey:(NSString *)key withBlock:(void (^)(NSData *imageData))block {}

简单的说 YYImageCache 就跟它的名字一样,负责缓存相关的活。

3.3.2 YYWebImageOperation

YYWebImageOperation 是 NSOperation 的子类,用于获取图片数据。下面看下对应的属性。

@property (nonatomic, strong, readonly)           NSURLRequest      *request;
@property (nullable, nonatomic, strong, readonly) NSURLResponse     *response;
@property (nullable, nonatomic, strong, readonly) YYImageCache      *cache;
@property (nonatomic, strong, readonly)           NSString          *cacheKey;
//图片加载时候的可选效果,比如模糊效果和渐变效果
@property (nonatomic, readonly)                   YYWebImageOptions options;
// 是否使用 NSURLCredential
@property (nonatomic) BOOL shouldUseCredentialStorage;
@property (nullable, nonatomic, strong) NSURLCredential *credential;

下面看下具体的方法

// 校验 Image 最后一个像素
static BOOL YYCGImageLastPixelFilled(CGImageRef image) {}
// 生成并返回 SOS Marker
static NSData *JPEGSOSMarker() {}
// 黑名单
static NSMutableSet *URLBlacklist;
// 用于黑名单的锁
static dispatch_semaphore_t URLBlacklistLock;
// 黑名单初始化
static void URLBlacklistInit() {}
// 确认是否在黑名单
static BOOL URLBlackListContains(NSURL *url) {}
// 黑名单添加
static void URLInBlackListAdd(NSURL *url) {}

// 下面还定义了一个代理类用作转发
@interface _YYWebImageWeakProxy : NSProxy
@property (nonatomic, weak, readonly) id target;

- (instancetype)initWithTarget:(id)target;
+ (instancetype)proxyWithTarget:(id)target;
@end

然后再看下定义的私有属性

// 分成三块,一块是比较通用的,第二块是针对渐进式的,第三块是回调相关的
@interface YYWebImageOperation() <NSURLConnectionDelegate>
@property (readwrite, getter=isExecuting) BOOL executing;
@property (readwrite, getter=isFinished) BOOL finished;
@property (readwrite, getter=isCancelled) BOOL cancelled;
@property (readwrite, getter=isStarted) BOOL started;
@property (nonatomic, strong) NSRecursiveLock *lock;
@property (nonatomic, strong) NSURLConnection *connection;
@property (nonatomic, strong) NSMutableData *data;//网络请求来的数据存在这
@property (nonatomic, assign) NSInteger expectedSize;//预计网络请求来的数据有多大
@property (nonatomic, assign) UIBackgroundTaskIdentifier taskID;

@property (nonatomic, assign) NSTimeInterval lastProgressiveDecodeTimestamp;
@property (nonatomic, strong) YYImageDecoder *progressiveDecoder;
@property (nonatomic, assign) BOOL progressiveIgnored;
@property (nonatomic, assign) BOOL progressiveDetected;
@property (nonatomic, assign) NSUInteger progressiveScanedLength;
@property (nonatomic, assign) NSUInteger progressiveDisplayCount;

@property (nonatomic, copy) YYWebImageProgressBlock progress;
@property (nonatomic, copy) YYWebImageTransformBlock transform;
@property (nonatomic, copy) YYWebImageCompletionBlock completion;
@end

然后再看下单例方法,其实跟AFN是类似的,也是运用了单例线程 + Runloop 开启保活的套路。
然后又因为 Autorelease 对象是在当前的 Runloop 迭代结束时释放的,这里又使得 Runloop 不会被停止,所以下面的实例化相关的操作就需要放到 @autoreleasepool 当中进行操作。

+ (void)_networkThreadMain:(id)object {
    @autoreleasepool {
        [[NSThread currentThread] setName:@"com.ibireme.webimage.request"];
        NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
        [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
        [runLoop run];
    }
}

+ (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;
}

// 获得用于 image 读取和解码使用的串行队列
+ (dispatch_queue_t)_imageQueue {
    #define MAX_QUEUE_COUNT 16
    static int queueCount;
    static dispatch_queue_t queues[MAX_QUEUE_COUNT];
    static dispatch_once_t onceToken;
    static int32_t counter = 0;
    dispatch_once(&onceToken, ^{
        queueCount = (int)[NSProcessInfo processInfo].activeProcessorCount;
        queueCount = queueCount < 1 ? 1 : queueCount > MAX_QUEUE_COUNT ? MAX_QUEUE_COUNT : queueCount;
        if ([UIDevice currentDevice].systemVersion.floatValue >= 8.0) {
            for (NSUInteger i = 0; i < queueCount; i++) {
                dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_UTILITY, 0);
                queues[i] = dispatch_queue_create("com.ibireme.image.decode", attr);
            }
        } else {
            for (NSUInteger i = 0; i < queueCount; i++) {
                queues[i] = dispatch_queue_create("com.ibireme.image.decode", DISPATCH_QUEUE_SERIAL);
                dispatch_set_target_queue(queues[i], dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0));
            }
        }
    });
    int32_t cur = OSAtomicIncrement32(&counter);
    if (cur < 0) cur = -cur;
    return queues[(cur) % queueCount];
    #undef MAX_QUEUE_COUNT
}
- (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;
}

// 销毁方法

- (void)dealloc {
    [_lock lock];
    if (_taskID != UIBackgroundTaskInvalid) {//如果有任务
        [_YYSharedApplication() endBackgroundTask:_taskID]; 就调用 application 去销毁任务
        _taskID = UIBackgroundTaskInvalid;
    }
    if ([self isExecuting]) {//如果被正在执行
        self.cancelled = YES;
        self.finished = YES;
        if (_connection) {//NSURLConnection 非空的时候
            [_connection cancel];
            if (![_request.URL isFileURL] && (_options & YYWebImageOptionShowNetworkActivity)) {//符合条件的话,就对当前网络请求的活动数量-1
                [YYWebImageManager decrementNetworkActivityCount];
            }
        }
        if (_completion) {
            @autoreleasepool {
                _completion(nil, _request.URL, YYWebImageFromNone, YYWebImageStageCancelled, nil);
            }
        }
    }
    [_lock unlock];
}

//销毁任务

- (void)_endBackgroundTask {}

下面是 NSOperation 相关的方法

// NSOperation 的开始方法,也就是表示图片加载请求的开始
// 加载开始后,如果位于后台并且后台任务过期就会主动结束这个任务

- (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) && _YYSharedApplication()) {
                    __weak __typeof__ (self) _self = self;
                    if (_taskID == UIBackgroundTaskInvalid) {
                        _taskID = [_YYSharedApplication() beginBackgroundTaskWithExpirationHandler:^{
                            __strong __typeof (_self) self = _self;
                            if (self) {
                                [self cancel];
                                self.finished = YES;
                            }
                        }];
                    }
                }
            }
        }
        [_lock unlock];
    }
}

- (void)cancel {
    [_lock lock];
    if (![self isCancelled]) {
        [super cancel];
        self.cancelled = YES;
        if ([self isExecuting]) {
            self.executing = NO;
            [self performSelector:@selector(_cancelOperation) onThread:[[self class] _networkThread] withObject:nil waitUntilDone:NO modes:@[NSDefaultRunLoopMode]];
        }
        if (self.started) {
            self.finished = YES;
        }
    }
    [_lock unlock];
}

// 下面这块是手动 KVO 通知,并且设置和获取的都是调用了递归锁

- (void)setExecuting:(BOOL)executing {
    [_lock lock];
    if (_executing != executing) {
        [self willChangeValueForKey:@"isExecuting"];
        _executing = executing;
        [self didChangeValueForKey:@"isExecuting"];
    }
    [_lock unlock];
}

- (BOOL)isExecuting {
    [_lock lock];
    BOOL executing = _executing;
    [_lock unlock];
    return executing;
}

- (void)setFinished:(BOOL)finished {}
- (BOOL)isFinished {}
- (void)setCancelled:(BOOL)cancelled {}
- (BOOL)isCancelled {}
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
    if ([key isEqualToString:@"isExecuting"] ||
        [key isEqualToString:@"isFinished"] ||
        [key isEqualToString:@"isCancelled"]) {
        return NO;
    }
    return [super automaticallyNotifiesObserversForKey:key];
}

- (BOOL)isConcurrent {
    return YES;//并发
}

- (BOOL)isAsynchronous {
    return YES;//异步
}

线程运行有关的

// 执行完成,关闭后台任务

- (void)_finish {
    self.executing = NO;
    self.finished = YES;
    [self _endBackgroundTask];
}

// 进行加载请求之前先判断下有没缓存
// 先内存缓存看下有没,然后再磁盘缓存看下
// 都没有的话就调用 _startRequest 加载请求

- (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];
}

// 

- (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 ? fileSize.unsignedIntegerValue : -1;
        }

        // 从网络加载图片
        [_lock lock];
        if (![self isCancelled]) {
            _connection = [[NSURLConnection alloc] initWithRequest:_request delegate:[_YYWebImageWeakProxy proxyWithTarget:self]];
            if (![_request.URL isFileURL] && (_options & YYWebImageOptionShowNetworkActivity)) {
                [YYWebImageManager incrementNetworkActivityCount];
            }
        }
        [_lock unlock];
    }
}

// 取消的操作
- (void)_cancelOperation {}
// 获取图片的时候发现磁盘缓存中有,就会调用这个方法
- (void)_didReceiveImageFromDiskCache:(UIImage *)image {}
// 网络请求获取图片之后调用这个方法
// 拿到 image 之后如果非空就添加到缓存,并调用 completion 回调
// 如果是空的,再看下有没开启黑名单,有开启就把这个 url 添加到黑名单
- (void)_didReceiveImageFromWeb:(UIImage *)image {
    @autoreleasepool {
        [_lock lock];
        if (![self isCancelled]) {
            if (_cache) {
                if (image || (_options & YYWebImageOptionRefreshImageCache)) {
                    NSData *data = _data;
                    dispatch_async([YYWebImageOperation _imageQueue], ^{
                        [_cache setImage:image imageData:data forKey:_cacheKey withType:YYImageCacheTypeAll];
                    });
                }
            }
            _data = nil;
            NSError *error = nil;
            if (!image) {
                error = [NSError errorWithDomain:@"com.ibireme.image" code:-1 userInfo:@{ NSLocalizedDescriptionKey : @"Web image decode fail." }];
                if (_options & YYWebImageOptionIgnoreFailedURL) {
                    if (URLBlackListContains(_request.URL)) {
                        error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:@{ NSLocalizedDescriptionKey : @"Failed to load URL, blacklisted." }];
                    } else {
                        URLInBlackListAdd(_request.URL);
                    }
                }
            }
            if (_completion) _completion(image, _request.URL, YYWebImageFromRemote, YYWebImageStageFinished, error);
            [self _finish];
        }
        [_lock unlock];
    }
}

NSURLConnectionDelegate 相关的

// 是否使用证书校验
- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection {
    return _shouldUseCredentialStorage;
}
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {}

// 如果 options 是使用 NSURLCache 就返回cachedResponse,否则就返回nil,使用自己的 YYImageCache
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse {
    if (!cachedResponse) return cachedResponse;
    if (_options & YYWebImageOptionUseNSURLCache) {
        return cachedResponse;
    } else {
        // ignore NSURLCache
        return nil;
    }
}

// 获取响应头后,把数据大小取出来,根据大小实例化 _data
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    @autoreleasepool {
        NSError *error = nil;
        if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
            NSHTTPURLResponse *httpResponse = (id) response;
            NSInteger statusCode = httpResponse.statusCode;
            if (statusCode >= 400 || statusCode == 304) {
                error = [NSError errorWithDomain:NSURLErrorDomain code:statusCode userInfo:nil];
            }
        }
        if (error) {
            [_connection cancel];
            [self connection:_connection didFailWithError:error];
        } else {
            if (response.expectedContentLength) {
                _expectedSize = (NSInteger)response.expectedContentLength;
                if (_expectedSize < 0) _expectedSize = -1;
            }
            _data = [NSMutableData dataWithCapacity:_expectedSize > 0 ? _expectedSize : 0];
            if (_progress) {
                [_lock lock];
                if (![self isCancelled]) _progress(0, _expectedSize);
                [_lock unlock];
            }
        }
    }
}

// 每次接受数据填充进去。
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    @autoreleasepool {
        [_lock lock];
        BOOL canceled = [self isCancelled];
        [_lock unlock];
        if (canceled) return;

        if (data) [_data appendData:data];
        if (_progress) {
            [_lock lock];
            if (![self isCancelled]) {
                _progress(_data.length, _expectedSize);
            }
            [_lock unlock];
        }

        /*--------------------------- progressive ----------------------------*/
        BOOL progressive = (_options & YYWebImageOptionProgressive) > 0;
        BOOL progressiveBlur = (_options & YYWebImageOptionProgressiveBlur) > 0;
        if (!_completion || !(progressive || progressiveBlur)) return;
        if (data.length <= 16) return;//数据过少或者数据过多的时候都没必要使用渐进式
        if (_expectedSize > 0 && data.length >= _expectedSize * 0.99) return;
        if (_progressiveIgnored) return;

        NSTimeInterval min = progressiveBlur ? MIN_PROGRESSIVE_BLUR_TIME_INTERVAL : MIN_PROGRESSIVE_TIME_INTERVAL;
        NSTimeInterval now = CACurrentMediaTime();
        if (now - _lastProgressiveDecodeTimestamp < min) return;//间隔过短没有必要进行处理

        if (!_progressiveDecoder) {
            _progressiveDecoder = [[YYImageDecoder alloc] initWithScale:[UIScreen mainScreen].scale];
        }
        [_progressiveDecoder updateData:_data final:NO];
        if ([self isCancelled]) return;

        if (_progressiveDecoder.type == YYImageTypeUnknown ||
            _progressiveDecoder.type == YYImageTypeWebP ||
            _progressiveDecoder.type == YYImageTypeOther) {//渐进式不支持 Webp 和未知类型
            _progressiveDecoder = nil;
            _progressiveIgnored = YES;
            return;
        }
        if (progressiveBlur) { //渐进模糊只支持 JPEG 和 PNG
            if (_progressiveDecoder.type != YYImageTypeJPEG &&
                _progressiveDecoder.type != YYImageTypePNG) {
                _progressiveDecoder = nil;
                _progressiveIgnored = YES;
                return;
            }
        }
        if (_progressiveDecoder.frameCount == 0) return;

        if (!progressiveBlur) {
            YYImageFrame *frame = [_progressiveDecoder frameAtIndex:0 decodeForDisplay:YES];
            if (frame.image) {
                [_lock lock];
                if (![self isCancelled]) {
                    _completion(frame.image, _request.URL, YYWebImageFromRemote, YYWebImageStageProgress, nil);
                    _lastProgressiveDecodeTimestamp = now;
                }
                [_lock unlock];
            }
            return;
        } else {
            if (_progressiveDecoder.type == YYImageTypeJPEG) {
                if (!_progressiveDetected) {
                    NSDictionary *dic = [_progressiveDecoder framePropertiesAtIndex:0];
                    NSDictionary *jpeg = dic[(id)kCGImagePropertyJFIFDictionary];
                    NSNumber *isProg = jpeg[(id)kCGImagePropertyJFIFIsProgressive];
                    if (!isProg.boolValue) {
                        _progressiveIgnored = YES;
                        _progressiveDecoder = nil;
                        return;
                    }
                    _progressiveDetected = YES;
                }
                // JPEG 中的 SOS 标识后面就是图片数据,这里
                NSInteger scanLength = (NSInteger)_data.length - (NSInteger)_progressiveScanedLength - 4;
                if (scanLength <= 2) return;
                NSRange scanRange = NSMakeRange(_progressiveScanedLength, scanLength);
                NSRange markerRange = [_data rangeOfData:JPEGSOSMarker() options:kNilOptions range:scanRange];
                _progressiveScanedLength = _data.length;
                if (markerRange.location == NSNotFound) return;
                if ([self isCancelled]) return;

            } else if (_progressiveDecoder.type == YYImageTypePNG) {
                if (!_progressiveDetected) {
                    NSDictionary *dic = [_progressiveDecoder framePropertiesAtIndex:0];
                    NSDictionary *png = dic[(id)kCGImagePropertyPNGDictionary];
                    NSNumber *isProg = png[(id)kCGImagePropertyPNGInterlaceType];
                    if (!isProg.boolValue) {
                        _progressiveIgnored = YES;
                        _progressiveDecoder = nil;
                        return;
                    }
                    _progressiveDetected = YES;
                }
            }
            //取出第一帧,然后做模糊处理
            YYImageFrame *frame = [_progressiveDecoder frameAtIndex:0 decodeForDisplay:YES];
            UIImage *image = frame.image;
            if (!image) return;
            if ([self isCancelled]) return;

            if (!YYCGImageLastPixelFilled(image.CGImage)) return;
            _progressiveDisplayCount++;

            CGFloat radius = 32;
            if (_expectedSize > 0) {
                radius *= 1.0 / (3 * _data.length / (CGFloat)_expectedSize + 0.6) - 0.25;
            } else {
                radius /= (_progressiveDisplayCount);
            }
            image = [image yy_imageByBlurRadius:radius tintColor:nil tintMode:0 saturation:1 maskImage:nil];

            if (image) {
                [_lock lock];
                if (![self isCancelled]) {
                    _completion(image, _request.URL, YYWebImageFromRemote, YYWebImageStageProgress, nil);
                    _lastProgressiveDecodeTimestamp = now;
                }
                [_lock unlock];
            }
        }
    }
}

// 请求完成之后的处理
// image 由 self.data 解码获得,并且默认去获得第一帧
// 最后再调用 _didReceiveImageFromWeb 方法
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {}

// 请求失败,ActivityCount 减少,然后需要加黑名单就加黑名单。
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{}

3.3.3 YYWebImageManager

typedef NS_OPTIONS(NSUInteger, YYWebImageOptions) {
    // 下载图片的时候在状态栏显示小菊花
    YYWebImageOptionShowNetworkActivity = 1 << 0,
    // 启用渐进式效果
    YYWebImageOptionProgressive = 1 << 1,
    // 启用渐进式的模糊效果
    YYWebImageOptionProgressiveBlur = 1 << 2,
    // 启用 NSURLCache ,不使用 YYImageCache
    YYWebImageOptionUseNSURLCache = 1 << 3,
    // 允许非法的证书
    YYWebImageOptionAllowInvalidSSLCertificates = 1 << 4,
    // 允许后台下载图片
    YYWebImageOptionAllowBackgroundTask = 1 << 5,
    // 使用 NSHTTPCookieStore 处理 cookies
    YYWebImageOptionHandleCookies = 1 << 6,
    // 刷新图片缓存
    YYWebImageOptionRefreshImageCache = 1 << 7,
    // 忽视磁盘缓存
    YYWebImageOptionIgnoreDiskCache = 1 << 8,
    // 忽视占位图
    YYWebImageOptionIgnorePlaceHolder = 1 << 9,
    // 不对图片进行解码,一般用于下载图片不进行展示
    YYWebImageOptionIgnoreImageDecoding = 1 << 10,
    // 不对动图进行解码
    YYWebImageOptionIgnoreAnimatedImage = 1 << 11,
    // 使用渐变显示的效果
    YYWebImageOptionSetImageWithFadeAnimation = 1 << 12,
    // 图片加载完的时候不显示到 View 上
    YYWebImageOptionAvoidSetImage = 1 << 13,
    // 图片加载失败的话就添加到黑名单,不会再去尝试
    YYWebImageOptionIgnoreFailedURL = 1 << 14,
};

typedef NS_ENUM(NSUInteger, YYWebImageFromType) {
    YYWebImageFromNone = 0,
    // 立即从内存缓存中获得
    YYWebImageFromMemoryCacheFast,
    // 从内存缓存中获得
    YYWebImageFromMemoryCache,
    // 从磁盘缓存中获得
    YYWebImageFromDiskCache,
    // 从远端中获得(web或者文件)
    YYWebImageFromRemote,
};

typedef NS_ENUM(NSInteger, YYWebImageStage) {
    // 图片下载中
    YYWebImageStageProgress  = -1,
    // 取消
    YYWebImageStageCancelled = 0,
    // 完成(失败或者成功)
    YYWebImageStageFinished  = 1,
};

// 远程图片下载的进度
typedef void(^YYWebImageProgressBlock)(NSInteger receivedSize, NSInteger expectedSize);
// 远程图片下载完之后可以使用这个 block 进行二次处理,裁剪等操作。
typedef UIImage * _Nullable (^YYWebImageTransformBlock)(UIImage *image, NSURL *url);
// 远程图片完成或者失败之后调用这个 block
typedef void (^YYWebImageCompletionBlock)(UIImage * _Nullable image,
                                          NSURL *url,
                                          YYWebImageFromType from,
                                          YYWebImageStage stage,
                                          NSError * _Nullable error);


@interface YYWebImageManager : NSObject

// 单例
+ (instancetype)sharedManager;
// 实例化方法
- (instancetype)initWithCache:(nullable YYImageCache *)cache
                        queue:(nullable NSOperationQueue *)queue;
// 返回一个 YYWebImageOperation 实例并马上开始下载图片
- (nullable YYWebImageOperation *)requestImageWithURL:(NSURL *)url
                                              options:(YYWebImageOptions)options
                                             progress:(nullable YYWebImageProgressBlock)progress
                                            transform:(nullable YYWebImageTransformBlock)transform
                                           completion:(nullable YYWebImageCompletionBlock)completion;
// 缓存
@property (nullable, nonatomic, strong) YYImageCache *cache;
// 队列,最后生成的 operation 会并发的跑在这个队列上
@property (nullable, nonatomic, strong) NSOperationQueue *queue;
// Transform Block
@property (nullable, nonatomic, copy) YYWebImageTransformBlock sharedTransformBlock;
// 请求超时时间,默认为15
@property (nonatomic) NSTimeInterval timeout;
// NSURLCredential 的 username
@property (nullable, nonatomic, copy) NSString *username;
// NSURLCredential 的 password
@property (nullable, nonatomic, copy) NSString *password;
// 请求头部,默认"Accept:image/webp,image/\*;q=0.8"
@property (nullable, nonatomic, copy) NSDictionary<NSString *, NSString *> *headers;
// 如果针对某个 url 需要添加和移除请求头部的话可以实现这个 block
@property (nullable, nonatomic, copy) NSDictionary<NSString *, NSString *> *(^headersFilter)(NSURL *url, NSDictionary<NSString *, NSString *> * _Nullable header);
// 如果需要定制缓存 key 的话可以实现这个 block
@property (nullable, nonatomic, copy) NSString *(^cacheKeyFilter)(NSURL *url);

// 返回请求头部
- (nullable NSDictionary<NSString *, NSString *> *)headersForURL:(NSURL *)url;
// 返回一个 url 的缓存 key
- (NSString *)cacheKeyForURL:(NSURL *)url;
// 增加 NetworkActivityCount 
+ (void)incrementNetworkActivityCount;
// 减少 NetworkActivityCount 
+ (void)decrementNetworkActivityCount;
// 获取当前 NetworkActivityCount
+ (NSInteger)currentNetworkActivityCount;

下面看下实现文件的内容

// 略
+ (instancetype)sharedManager {}
// 区别就是会根据是否支持 Webp 来设置不同的 header,YYImageWebPAvailable 这个宏定义是由 YYImage 提供的
- (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;
}

// 根据 url 生成 request
// 然后根据 request 生成 operation
// 然后,如果有 queue 的话就扔进去开跑,没有的话就 start
- (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) {//需要进行认证的话,就传入 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;
}

// 下面两个同理,根据 url 返回对应需要的信息,如果有实现对应的 filter 就使用 filter
- (NSDictionary *)headersForURL:(NSURL *)url {
    if (!url) return nil;
    return _headersFilter ? _headersFilter(url, _headers) : _headers;
}
- (NSString *)cacheKeyForURL:(NSURL *)url {
    if (!url) return nil;
    return _cacheKeyFilter ? _cacheKeyFilter(url) : url.absoluteString;
}

上面的代码看起来是不是十分简单,Manager 干的事情很少,就是拿到 url 然后返回 operation ,并让它跑起来。

下面我们来聊聊小菊花,也就是状态栏上面的一直转啊转的小菊花,其实看过 AFN 源码的朋友应该是很清楚这块应该怎么做的。这里每进行一次图片加载,也就是进行了一次网络请求,会设置一个变量来计数着,完成一次就对这个变量-1,下面来看看这个小菊花相关的代码。

// 这里定义了一个对象来作为网络请求数的信息载体
@interface _YYWebImageApplicationNetworkIndicatorInfo : NSObject
@property (nonatomic, assign) NSInteger count;
@property (nonatomic, strong) NSTimer *timer;
@end
@implementation _YYWebImageApplicationNetworkIndicatorInfo
@end

// 使用 Associated Object 来做关联
+ (_YYWebImageApplicationNetworkIndicatorInfo *)_networkIndicatorInfo {
    return objc_getAssociatedObject(self, @selector(_networkIndicatorInfo));
}
+ (void)_setNetworkIndicatorInfo:(_YYWebImageApplicationNetworkIndicatorInfo *)info {
    objc_setAssociatedObject(self, @selector(_networkIndicatorInfo), info, OBJC_ASSOCIATION_RETAIN);
}
// 这个方法就是对小菊花进行操作,但是是由 _changeNetworkActivityCount 的 timer 来触发
+ (void)_delaySetActivity:(NSTimer *)timer {
    UIApplication *app = _YYSharedApplication();
    if (!app) return;

    NSNumber *visiable = timer.userInfo;
    if (app.networkActivityIndicatorVisible != visiable.boolValue) {
        [app setNetworkActivityIndicatorVisible:visiable.boolValue];
    }
    [timer invalidate];
}
// 修改当前活动请求的数量
+ (void)_changeNetworkActivityCount:(NSInteger)delta {
    if (!_YYSharedApplication()) return;

    void (^block)() = ^{
        _YYWebImageApplicationNetworkIndicatorInfo *info = [self _networkIndicatorInfo];
        if (!info) {
            info = [_YYWebImageApplicationNetworkIndicatorInfo new];
            [self _setNetworkIndicatorInfo:info];
        }
        NSInteger count = info.count;
        count += delta;
        info.count = count;
        [info.timer invalidate];
        info.timer = [NSTimer timerWithTimeInterval:kNetworkIndicatorDelay target:self selector:@selector(_delaySetActivity:) userInfo:@(info.count > 0) repeats:NO];// 对设置增加了延迟。额外提一下,YYWebImage 的 ActivationDelay 是 1/30,而 AFN 就比较细分,还有一个 CompletionDelay 的概念,有兴趣的朋友可以看下 AFN 的 AFNetworkActivityIndicatorManager 
        [[NSRunLoop mainRunLoop] addTimer:info.timer forMode:NSRunLoopCommonModes];// 如果不设置这个 mode 的话,会被UI操作所阻塞
    };
    if ([NSThread isMainThread]) {
        block();
    } else {
        dispatch_async(dispatch_get_main_queue(), block);
    }
}
//下面分别是增加、减少和获取
+ (void)incrementNetworkActivityCount {
    [self _changeNetworkActivityCount:1];
}
+ (void)decrementNetworkActivityCount {
    [self _changeNetworkActivityCount:-1];
}
+ (NSInteger)currentNetworkActivityCount {
    _YYWebImageApplicationNetworkIndicatorInfo *info = [self _networkIndicatorInfo];
    return info.count;
}

3.3.4 _YYWebImageSetter

这个类的定义就是一个私有类,用在 image 相关的 categories。

@interface _YYWebImageSetter : NSObject
// 图片的 url
@property (nullable, nonatomic, readonly) NSURL *imageURL;
// 当前的唯一标识
@property (nonatomic, readonly) int32_t sentinel;

// 创建一个 operation 并返回唯一标示
- (int32_t)setOperationWithSentinel:(int32_t)sentinel
                                url:(nullable NSURL *)imageURL
                            options:(YYWebImageOptions)options
                            manager:(YYWebImageManager *)manager
                           progress:(nullable YYWebImageProgressBlock)progress
                          transform:(nullable YYWebImageTransformBlock)transform
                         completion:(nullable YYWebImageCompletionBlock)completion;

// 取消请求并返回唯一标识,imageUrl 会设置为 nil
- (int32_t)cancel;
// 取消请求并返回唯一标识,imageUrl 会设置为新值
- (int32_t)cancelWithNewURL:(nullable NSURL *)imageURL;
// 用于设置图片的队列
+ (dispatch_queue_t)setterQueue;
@end
- (instancetype)init {// 初始化
    self = [super init];
    _lock = dispatch_semaphore_create(1);
    return self;
}
// 返回 _imageURL
- (NSURL *)imageURL {}
// 销毁+取消请求,标记自增
- (void)dealloc {
    OSAtomicIncrement32(&_sentinel);
    [_operation cancel];
}

- (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;
    }
    // 得到 operation,其实 setter 的作用就是持有 operation,在需要的时候可以 cancel 掉。
    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.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;
}

- (int32_t)cancel {
    return [self cancelWithNewURL:nil];
}
// 每次取消也会对标识就行自增
- (int32_t)cancelWithNewURL:(NSURL *)imageURL {
    int32_t sentinel;
    dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER);
    if (_operation) {
        [_operation cancel];
        _operation = nil;
    }
    _imageURL = imageURL;
    sentinel = OSAtomicIncrement32(&_sentinel);
    dispatch_semaphore_signal(_lock);
    return sentinel;
}
// 返回队列
+ (dispatch_queue_t)setterQueue {
    static dispatch_queue_t queue;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        queue = dispatch_queue_create("com.ibireme.webimage.setter", DISPATCH_QUEUE_SERIAL);
        dispatch_set_target_queue(queue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
    });
    return queue;
}

3.3.5 UIImageView+YYWebImage

接下来就看看主要使用的这个方法

- (void)yy_setImageWithURL:(NSURL *)imageURL
               placeholder:(UIImage *)placeholder
                   options:(YYWebImageOptions)options
                   manager:(YYWebImageManager *)manager
                  progress:(YYWebImageProgressBlock)progress
                 transform:(YYWebImageTransformBlock)transform
                completion:(YYWebImageCompletionBlock)completion {
    if ([imageURL isKindOfClass:[NSString class]]) imageURL = [NSURL URLWithString:(id)imageURL];
    manager = manager ? manager : [YYWebImageManager sharedManager];

    _YYWebImageSetter *setter = objc_getAssociatedObject(self, &_YYWebImageSetterKey);
    if (!setter) {
        setter = [_YYWebImageSetter new];
        objc_setAssociatedObject(self, &_YYWebImageSetterKey, setter, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    // 先取消原先的 url
    int32_t sentinel = [setter cancelWithNewURL:imageURL];

    _yy_dispatch_sync_on_main_queue(^{
        if ((options & YYWebImageOptionSetImageWithFadeAnimation) &&
            !(options & YYWebImageOptionAvoidSetImage)) {
            if (!self.highlighted) {
                [self.layer removeAnimationForKey:_YYWebImageFadeAnimationKey];
            }
        }
        // url 为 nil 就走设置 placeholder 流程
        if (!imageURL) {
            if (!(options & YYWebImageOptionIgnorePlaceHolder)) {
                self.image = placeholder;
            }
            return;
        }

        // 从内存中拿到缓存
        UIImage *imageFromMemory = nil;
        if (manager.cache &&
            !(options & YYWebImageOptionUseNSURLCache) &&
            !(options & YYWebImageOptionRefreshImageCache)) {
            imageFromMemory = [manager.cache getImageForKey:[manager cacheKeyForURL:imageURL] withType:YYImageCacheTypeMemory];
        }
        if (imageFromMemory) {
            if (!(options & YYWebImageOptionAvoidSetImage)) {
                self.image = imageFromMemory;
            }
            if(completion) completion(imageFromMemory, imageURL, YYWebImageFromMemoryCacheFast, YYWebImageStageFinished, nil);
            return;
        }
        // 走到这里就代表 url 非空,然后先走设置 placeholder 流程
        if (!(options & YYWebImageOptionIgnorePlaceHolder)) {
            self.image = placeholder;
        }

        __weak typeof(self) _self = self;
        dispatch_async([_YYWebImageSetter setterQueue], ^{
            YYWebImageProgressBlock _progress = nil;
            // progress 相关
            if (progress) _progress = ^(NSInteger receivedSize, NSInteger expectedSize) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    progress(receivedSize, expectedSize);
                });
            };

            __block int32_t newSentinel = 0;// 初始化标识
            __block __weak typeof(setter) weakSetter = nil;
            YYWebImageCompletionBlock _completion = ^(UIImage *image, NSURL *url, YYWebImageFromType from, YYWebImageStage stage, NSError *error) {
                __strong typeof(_self) self = _self;
                BOOL setImage = (stage == YYWebImageStageFinished || stage == YYWebImageStageProgress) && image && !(options & YYWebImageOptionAvoidSetImage);
                dispatch_async(dispatch_get_main_queue(), ^{
                    BOOL sentinelChanged = weakSetter && weakSetter.sentinel != newSentinel;// 如果为同一个标识,就没啥问题,不同的话就是取消请求
                    if (setImage && self && !sentinelChanged) {
                        BOOL showFade = ((options & YYWebImageOptionSetImageWithFadeAnimation) && !self.highlighted);
                        if (showFade) {// Fade 动画
                            CATransition *transition = [CATransition animation];
                            transition.duration = stage == YYWebImageStageFinished ? _YYWebImageFadeTime : _YYWebImageProgressiveFadeTime;
                            transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
                            transition.type = kCATransitionFade;
                            [self.layer addAnimation:transition forKey:_YYWebImageFadeAnimationKey];
                        }
                        self.image = image;
                    }
                    if (completion) {
                        if (sentinelChanged) {
                            completion(nil, url, YYWebImageFromNone, YYWebImageStageCancelled, nil);
                        } else {
                            completion(image, url, from, stage, error);
                        }
                    }
                });
            };

            // setter 生成 request 并调用,然后返回标识
            newSentinel = [setter setOperationWithSentinel:sentinel url:imageURL options:options manager:manager progress:_progress transform:transform completion:_completion];
            weakSetter = setter;
        });
    });
}

主要内容就是这个方法,其他的都是类似的,像 CALayer+YYWebImageMKAnnotationView+YYWebImageUIButton+YYWebImage 的方法跟这个都基本一样,就不赘述了。

3.3.6 UIImage+YYWebImage

提供很多跟 UIImage 相关的处理方法。
我懒。。。。这个就略过了,以后有时间再补充。

参考文档

  1. AFNetworking源码阅读(一)
2017-01-04 11:16264