圖片下載控件:從0到1

本文是學(xué)習(xí) SDWebImage 的產(chǎn)物,如果有不對(duì)的地方,歡迎指正。

客戶端開發(fā)中,圖片下載控件一定是工程里比不可少的,它的重要性不亞于網(wǎng)絡(luò)庫。下面將一步一步的從開始最簡單的圖片下載到最后的完整控件,來剖析下載控件之實(shí)現(xiàn)。

  1. 牛刀小試

    圖片下載需要一個(gè)url,根據(jù)url取到內(nèi)容,最后展示到控件上。將這個(gè)通過url下載圖片的過程封裝成為一個(gè)UIImageView的擴(kuò)展方法再合適不過了。代碼如下:

     @interface UIImageView (XXXCatorgory)
     - (void)XXX_setImageWithURL:(NSURL *)URL;
     @end
    
     @implementation UIImageView (XXXCatorgory)
     - (void)XXX_setImageWithURL:(NSURL *)URL {
         NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithURL:URL completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
             if (!error && data) {
                 self.image = [UIImage imageWithData:data];
             }
         }];
         [task resume];
     }   
     @end
    
  2. 漸入佳境

    上面的牛刀小試,已經(jīng)完成了最簡單的圖片下載封裝。有了這個(gè)擴(kuò)展,只要有url,就可以完成下載功能了。但是,是不是就這么簡單呢,這樣的封裝又會(huì)有什么樣的問題?

    一個(gè)很明顯的,是所有的操作,都在主線程上完成的。程序的主線程是如此寶貴,以至于它一旦忙碌起來,可能會(huì)造成界面卡頓。雖然NSURLSession的下載是異步的,可是如果界面上有很多張圖片要獲取,就會(huì)在主線程上起多個(gè)任務(wù),肯定是不合適的。下面我們將下載操作放到異步線程,只把必要的UI操作放回主線程中。

     - (void)XXX_setImageWithURL:(NSURL *)URL {
         dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
             NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithURL:URL completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                 if (!error && data) {
                     dispatch_async(dispatch_get_main_queue(), ^{
                         self.image = [UIImage imageWithData:data];
                     });
                 }
             }];
             [task resume];
         });
     }
    

    問題依然存在,這樣的任務(wù)一旦開始就無法取消了,而且多個(gè)任務(wù)可能會(huì)起很多線程。因此有必要將所有的下載任務(wù)放到一個(gè)獨(dú)立的地方從而方便控制——下載器應(yīng)運(yùn)而生,它擁有一個(gè)隊(duì)列,能夠控制并發(fā),并且返回下載操作。由于代碼較多,下面只放頭文件了,完整代碼放在文末的鏈接里:

     @interface XXXDownloader : NSObject
     
     @property(nonatomic) NSInteger maxConcurrent;
    
     + (instancetype)sharedDownloader;
    
     - (NSOperation *)downloadOperationWithURL:(NSURL *)URL completionHandler:(void(^)(NSData *data, NSError *error))handler;
    
     @end
    

將下載操作設(shè)置為圖片的關(guān)聯(lián)對(duì)象,此時(shí)UIImageView的方法就變成下面這樣:

    - (void)XXX_setImageWithURL:(NSURL *)URL {
        NSOperation *op = self.downloadOperation;
        if (op && !op.finished) {
            [op cancel];
        }
        op = [[XXXDownloader sharedDownloader] downloadOperationWithURL:URL completionHandler:^(NSData *data, NSError *error) {
            dispatch_async(dispatch_get_main_queue(), ^{
                if (!error && data) {
                    self.image = [UIImage imageWithData:data];
                }
            });
        }];
        [self setDownloadOperation:op];
    }

至此,已經(jīng)完成下載控件的初步封裝。所有跟下載相關(guān)的操作,比如對(duì)不合格的URL進(jìn)行過濾,設(shè)置下載超時(shí)時(shí)間等,都可以放到下載器里完成。
  1. 鋒芒畢露

    完成以上兩步是不是就大功告成了呢?并不是,我們還沒有使用緩存呢!怎么可以容忍同一個(gè)圖片下載兩次相同的url呢。緩存分為兩種,內(nèi)存緩存和磁盤緩存。下面我們?yōu)橄螺d控件加入緩存,很明顯,它也應(yīng)該是個(gè)獨(dú)立的系統(tǒng),這樣能夠方便的控制緩存大小,從而可以在適當(dāng)?shù)臅r(shí)機(jī)清除。

     @interface XXXImageCache : NSObject
    
     + (instancetype)sharedCache;
    
     - (void)cacheData:(UIImage *)image forKey:(NSString *)key;
     - (NSData *)cachedData:(NSString *)key;
    
     - (void)clearMemCache;
     - (void)clearDiskCache;
    
     @end
    

    可以在下載器下載完成后進(jìn)行緩存,而在UIImageView的方法里判斷緩存是否存在,代碼如下:

     - (void)XXX_setImageWithURL:(NSURL *)URL {
         NSData *data = [[XXXImageCache sharedCache] cachedData:[URL absoluteString]];
         if (image) {
             self.image = [UIImage imageWithData:data];
             return;
         }
     
         NSOperation *op = self.downloadOperation;;
         if (op && !op.finished) {
             [task cancel];
         }
         op = [[TSDDownloader sharedDownloader] downloadOperationWithURL:URL completionHandler:^(NSData *data, NSError *error) {
             dispatch_async(dispatch_get_main_queue(), ^{
                 if (!error && data) {
                     self.image = [UIImage imageWithData:data];
                 }
             });
         }];
         [self setDownloadOperation:op];
     }
    

總結(jié)

從上面可以看出,實(shí)現(xiàn)一個(gè)基本的圖片下載控件的思路,先是簡單的下載封裝,然后考慮異步操作并且控制并發(fā),最后加入緩存系統(tǒng)。SDWebImage實(shí)現(xiàn)了更豐富的功能,如placeholder、下載進(jìn)度、支持更多圖片類型等,但是道理應(yīng)該是相通的。

這篇文章只是簡單的介紹了從0到1的過程,從1到100,還有很長的路要走。希望后面還有機(jī)會(huì)繼續(xù)來完善這篇文章。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,885評(píng)論 6 541
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,312評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,993評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,667評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,410評(píng)論 6 411
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,778評(píng)論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,775評(píng)論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,955評(píng)論 0 289
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,521評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,266評(píng)論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,468評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,998評(píng)論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,696評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,095評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,385評(píng)論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,193評(píng)論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,431評(píng)論 2 378

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