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


接下來看圖片的下載

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 也取消掉

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末空凸,一起剝皮案震驚了整個濱河市嚎花,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌呀洲,老刑警劉巖紊选,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異道逗,居然都是意外死亡兵罢,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進(jìn)店門滓窍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來卖词,“玉大人,你說我怎么就攤上這事吏夯〈蓑冢” “怎么了?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵噪生,是天一觀的道長裆赵。 經(jīng)常有香客問我,道長跺嗽,這世上最難降的妖魔是什么顾瞪? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮抛蚁,結(jié)果婚禮上陈醒,老公的妹妹穿的比我還像新娘。我一直安慰自己瞧甩,他們只是感情好钉跷,可當(dāng)我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著肚逸,像睡著了一般爷辙。 火紅的嫁衣襯著肌膚如雪彬坏。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天膝晾,我揣著相機與錄音栓始,去河邊找鬼。 笑死血当,一個胖子當(dāng)著我的面吹牛幻赚,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播臊旭,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼落恼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了离熏?” 一聲冷哼從身側(cè)響起佳谦,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎滋戳,沒想到半個月后钻蔑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡奸鸯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年矢棚,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片府喳。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蘑拯,靈堂內(nèi)的尸體忽然破棺而出钝满,到底是詐尸還是另有隱情,我是刑警寧澤申窘,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布弯蚜,位于F島的核電站,受9級特大地震影響剃法,放射性物質(zhì)發(fā)生泄漏碎捺。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一贷洲、第九天 我趴在偏房一處隱蔽的房頂上張望收厨。 院中可真熱鬧,春花似錦优构、人聲如沸诵叁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拧额。三九已至碑诉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間侥锦,已是汗流浹背进栽。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留恭垦,地道東北人快毛。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像署照,于是被迫代替她去往敵國和親祸泪。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內(nèi)容