YYWebImage工作原理介紹-----下載單張圖片

YYWebImage簡介

YYWebImage是由ibireme開發(fā)的YYkit其中的一個庫霉囚,專門處理圖像榜跌。github傳送門:YYWebImage钓葫。他的出現(xiàn)是為了替代 SDWebImage票顾、PINRemoteImage豆同、FLAnimatedImage 等開源框架含鳞。在YYImage出來之前,我們的圖片下載和動態(tài)圖片展示需要SD和FL兩個庫去處理≡姹В現(xiàn)在YYImage的出現(xiàn)使得統(tǒng)一圖像庫成為可能佳晶。這篇文章會根據(jù)YYImage下載單張圖的生命周期為主線簡單的介紹一下轿秧,YYImage各個模塊是如何協(xié)同合作的淤刃。

YYWebImage使用

先引入頭文件:

 #import <YYWebImage/YYWebImage.h>

然后創(chuàng)建一個UIImageVIew,YYWebImage有對 UIImageVIew 的Category 方法支持津滞。所以只要一句話:

imvAd.cbd_imageURL=[NSURL URLWithString:@"http://ss.bdimg.com/static/superman/img/logo/bd_logo1_31bdc765.png"];

這樣圖片就下載下來并展示出來触徐,api相當簡單撞鹉。接下來我們就深入源代碼鸟雏,看看YYimage到底做了些什么览祖。

UIImageView+YYWebImage類

當我們點進方法以后展蒂,發(fā)現(xiàn)這是一個Category 方法,里面提供了很多方法柳骄,單都是基于一個方法擴展的:

- (void)setYy_imageURL:(NSURL *)imageURL {
    [self yy_setImageWithURL:imageURL
                 placeholder:nil
                     options:kNilOptions
                     manager:nil
                    progress:nil
                   transform:nil
                  completion:nil];
}

里面可以傳圖片的url耐薯,默認圖片鸠踪,動畫效果類型营密,下載類型评汰,下載管理器被去,提供的block:進度條回調,完成回調奖唯。

這個方法是整個Category的核心惨缆,代碼比較長,我們分開看丰捷。
首先是初始化一個YYWebImageManager坯墨,然后動態(tài)的添加_YYWebImageSetter屬性,為的是管控整個YYImage的下載病往,查找有沒有相同的url在下載捣染,如果有的話就要取消操作,確保同一個url只有一個隊列在下載處理:

_YYWebImageSetter *setter = objc_getAssociatedObject(self, &_YYWebImageSetterKey);
    if (!setter) {
        setter = [_YYWebImageSetter new];
        objc_setAssociatedObject(self, &_YYWebImageSetterKey, setter, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    int32_t sentinel = [setter cancelWithNewURL:imageURL];

緊接著YYImage會切到主線程中停巷,做一些配置耍攘,設置一下動畫,設置一下默認圖片,根據(jù)剛才的options從不同的地方獲取圖片:

if ((options & YYWebImageOptionSetImageWithFadeAnimation) &&
            !(options & YYWebImageOptionAvoidSetImage)) {
            if (!self.highlighted) {
                [self.layer removeAnimationForKey:_YYWebImageFadeAnimationKey];
            }
        }
        
        if (!imageURL) {
            if (!(options & YYWebImageOptionIgnorePlaceHolder)) {
                self.image = placeholder;
            }
            return;
        }
        
        // get the image from memory as quickly as possible
        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;
        }
        
        if (!(options & YYWebImageOptionIgnorePlaceHolder)) {
            self.image = placeholder;
        }

這里要注意的是他會在YYImageCacheMemory中尋找一次圖片,也就是他默認先在內存中找一下。但是默認的方法中卖氨,我們的圖片是存在YYImageCacheDisk中的系吭。我們點開路徑可以看到则吟,他是在Library/Caches/com.ibireme.yykit/images中的數(shù)據(jù)庫里寨昙,我們查看一下數(shù)據(jù)庫發(fā)現(xiàn):



已經(jīng)存好了。(因為我這里已經(jīng)下載過這張圖片了)

所以現(xiàn)在YYImage并沒有找到圖片缆巧,繼續(xù)往執(zhí)行,他開出一個異步線程進行下載:

dispatch_async([_YYWebImageSetter setterQueue], ^{
            YYWebImageProgressBlock _progress = nil;
            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) {
                            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);
                        }
                    }
                });
            };
            
            newSentinel = [setter setOperationWithSentinel:sentinel url:imageURL options:options manager:manager progress:_progress transform:transform completion:_completion];
            weakSetter = setter;
        });

這里的核心是:

newSentinel = [setter setOperationWithSentinel:sentinel url:imageURL options:options manager:manager progress:_progress transform:transform completion:_completion];

YYWebImageOperation.h類

點進去以后看祠够,發(fā)現(xiàn)又是一坨虑瀑,仔細看會發(fā)現(xiàn)痛侍,大部分是賦值和一些判斷君丁,最關鍵的創(chuàng)建一個請求去獲取圖片:

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

作者對NSOperation進行了自定義扒最,他創(chuàng)建了一個YYWebImageOperation强挫,這是YYImage請求的核心稠诲。首先他重寫了
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) && _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];
    }
}

這個start方法主要是對隊列的一些控制跨释,他會再分出一個網(wǎng)絡線程networkThread去做網(wǎng)絡請求工作缆娃。我們順著往下看崇渗,他進入了:

-(void)_startOperation

這里還沒進入請求金拒,YYImage會去查看硬盤內存中的圖片幢码,如果有就會執(zhí)行:

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

如果沒有的話贞铣,就會去執(zhí)行_startRequest方法辕坝,然后我們就看到了最重要的一段代碼:

if (![self isCancelled]) {
            _connection = [[NSURLConnection alloc] initWithRequest:_request delegate:[_YYWebImageWeakProxy proxyWithTarget:self]];
            if (![_request.URL isFileURL] && (_options & YYWebImageOptionShowNetworkActivity)) {
                [YYWebImageManager incrementNetworkActivityCount];
            }
        }

這里請求還是用的快要淘汰的NSURLConnection纺酸,- -讓人潸然淚下....接下來就是一些回調了用含。整個流程結束橡类!

整個流程圖如下:


總結

由此看出YYImage使用非常簡單,但內部構造比較復雜咆课。主要的核心模塊是:_YYWebImageSetter末誓,YYImageCache扯俱,YYWebImageManager,YYWebImageOperation基显。分工有序蘸吓,結構清晰,美中不足的是請求方式還是基于快要淘汰的NSURLConnection撩幽。不過要修改起來也比較容易库继,之后我會對YYImage的其他功能做介紹分析,如果你感興趣可以關注我窜醉。如果有任何問題可以留言我宪萄。

感謝

YYImage:https://github.com/ibireme/YYWebImage

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市榨惰,隨后出現(xiàn)的幾起案子拜英,更是在濱河造成了極大的恐慌,老刑警劉巖琅催,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件居凶,死亡現(xiàn)場離奇詭異,居然都是意外死亡藤抡,警方通過查閱死者的電腦和手機侠碧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來缠黍,“玉大人弄兜,你說我怎么就攤上這事〈墒剑” “怎么了替饿?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長贸典。 經(jīng)常有香客問我视卢,道長,這世上最難降的妖魔是什么瓤漏? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任腾夯,我火速辦了婚禮,結果婚禮上蔬充,老公的妹妹穿的比我還像新娘。我一直安慰自己班利,他們只是感情好饥漫,可當我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著罗标,像睡著了一般庸队。 火紅的嫁衣襯著肌膚如雪积蜻。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天彻消,我揣著相機與錄音竿拆,去河邊找鬼。 笑死宾尚,一個胖子當著我的面吹牛丙笋,可吹牛的內容都是我干的。 我是一名探鬼主播煌贴,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼御板,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了牛郑?” 一聲冷哼從身側響起怠肋,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎淹朋,沒想到半個月后笙各,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡础芍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年杈抢,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片者甲。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡春感,死狀恐怖,靈堂內的尸體忽然破棺而出虏缸,到底是詐尸還是另有隱情鲫懒,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布刽辙,位于F島的核電站窥岩,受9級特大地震影響,放射性物質發(fā)生泄漏宰缤。R本人自食惡果不足惜颂翼,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望慨灭。 院中可真熱鬧朦乏,春花似錦、人聲如沸氧骤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽筹陵。三九已至刽锤,卻和暖如春镊尺,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背并思。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工庐氮, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人宋彼。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓弄砍,卻偏偏與公主長得像,于是被迫代替她去往敵國和親宙暇。 傳聞我的和親對象是個殘疾皇子输枯,可洞房花燭夜當晚...
    茶點故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內容