接下来看图片的下载
YYWebImageManager
这个是网络下载管理类,管理网络下载和缓存
结构
单例管理 的类
+ (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中执行的,是并且执行。
结构
初始化
- (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一样的处理方式,不做介绍了
我认为这个类还是做个流程图看看吧
大概流程就是这个样子的。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 也取消掉