接下來看圖片的下載
YYWebImageManager
這個是網(wǎng)絡(luò)下載管理類紊扬,管理網(wǎng)絡(luò)下載和緩存
結(jié)構(gòu)
單例管理 的類
+ (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;
}
接下來看看網(wǎng)絡(luò)請求部分
1.獲取request
2.獲取請求圖片的operation
3.在queue中加入到operation 中發(fā)起任務(wù)酿秸。
這里主要是看operation 是如何下載圖片的
YYWebImageOperation
具體請求圖片就是在operation中執(zhí)行的绷雏,是并且執(zhí)行爽雄。
結(jié)構(gòu)
初始化
- (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;
}
基本初始化椭微。相關(guān)變量賦值。沒有過多操作
我們知道這個類是NSOpertion的子類盲链,并且是異步的。所以從start 函數(shù)啟動
- (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 標(biāo)記operation started
2.檢查operation 當(dāng)前狀態(tài)
3.當(dāng)operation 取消了迟杂, 就執(zhí)行 -(void)_cancelOperation 操作
《1》執(zhí)行_cancelOperation 操作
《2》標(biāo)記 operation 結(jié)束
4當(dāng)operation isReady?
《1》沒有reqeust, 標(biāo)記operation 結(jié)束刽沾,并且回調(diào)completion完成
《2》有request,標(biāo)記operation executing 排拷。執(zhí)行_startOperation?
《3》當(dāng)允許后臺執(zhí)行任務(wù)侧漓,判斷_taskID 是否標(biāo)記過,沒有就標(biāo)記后臺任務(wù)完成后就取消operation监氢,并且標(biāo)記 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,網(wǎng)絡(luò)線程浪腐,并且開始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執(zhí)行的
1.檢查operation是否取消
2.從緩存中查找,因為這里是在線程中议街,所以用的同步查詢
《1》先從內(nèi)存中查找泽谨,找到就返回
《2》再從磁盤上查找,找到就返回特漩,在磁盤找的時候是異步的吧雹。
《3》沒有執(zhí)行網(wǎng)絡(luò)請求
3.執(zhí)行網(wǎng)絡(luò)請求
// 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];
? ? }
}
執(zhí)行網(wǎng)絡(luò)請求
1 這里檢測operation是否被取消了
2.url是否再黑名單中,在黑名單就返回error
3.用NSURLConnection 連接
4.打開網(wǎng)絡(luò)請求指示器
接下來看看網(wǎng)絡(luò)請求的回調(diào)函數(shù)
網(wǎng)絡(luò)請求的回調(diào)是SDWebimage一樣的處理方式涂身,不做介紹了
我認(rèn)為這個類還是做個流程圖看看吧
大概流程就是這個樣子的雄卷。networkThread 是管理網(wǎng)絡(luò)下載或者查詢數(shù)據(jù)的
而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 記錄當(dāng)前sentinel蛤售,當(dāng)做一個標(biāo)記吧
這里主要的函數(shù)是
- (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 改變了妒潭,那么久說明取消操作,調(diào)用完成block鳄炉,返回上一次請求的_sentienl
2.sentinel 沒變化杜耙,獲取operation?
《1 》要是operation 有,說明在下載拂盯,
《2》要是沒有operation 佑女,那么說明沒有下載圖片,那么就調(diào)用完成block
3.要是sentinel 不變谈竿,那么取消上一個的operation团驱,將sentinel 增加
4.要是sentinel變化了。當(dāng)前的operation 也取消掉