圖片下載

NSOperation下載圖片

首先就是根據圖片的url去images中取圖片,如果存在直接就將圖片顯示到cell上,問題是不存在該怎么處理?現在images里面沒有圖片,里面給界面顯示一個占位的圖片,然后根據這張圖片的url也就是圖片的唯一標識符去查看操作隊列中是否存在下載標識符為url的圖片的操作,如果操作隊列里面已經有了下載該張圖片的操作,那么等待操作隊列開啟這個操作的start方法即可,可是如果操作隊列中沒有下載該張圖片的操作,那么就需要先創建一個下載該張圖片的操作,最好給這個操作也添加一個唯一標志符,避免重復添加下載該圖片的操作對象到同一個操作隊列之中。現在已經將下載該圖片的操作對象添加到操作隊列之中了,只等前一個操作對象執行完畢就會取出該操作對象并調用start方法,當然啦,前提是這個操作隊列設置的最大允許并發數是1。當這個操作對象調用start方法執行完下載圖片的任務之后取出下載成功的圖片展示到界面上,剩下的最后一步尤其重要,就是將已經執行完畢的操作對象從操作隊列中移除,可是我就疑惑了,為什么操作隊列不能再操作對象執行完畢之后自動移除這個操作對象呢,簡直就是不科學呀!居然要我們手動去操作隊列里面移除這個已經執行完畢的操作對象,難道只是因為操作隊列無法監聽和管理操作對象里面封裝的任務的執行狀態。這也不科學呀,操作對列這么NB,這點小事不可能做不到呀!當然啦,我們經常用到的圖片下載都是用沙盒來緩存我們所下載的圖片的,知道圖片的url后也首先是看看沙盒里面有沒有,自然,在圖片下載成功的時候,也需要特別注意先要把下載成功的圖片存儲到沙盒里。

// 自己下載圖片
-(void)downloadImage{
    // 先從內存緩存中取出圖片
    UIImage *image = self.images[app.icon];
    if (image) { // 內存中有圖片
        cell.imageView.image = image;
    } else {  // 內存中沒有圖片
        // 獲得Library/Caches文件夾
        NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
        // 獲得文件名
        NSString *filename = [app.icon lastPathComponent];
        // 計算出文件的全路徑
        NSString *file = [cachesPath stringByAppendingPathComponent:filename];
        // 加載沙盒的文件數據
        NSData *data = [NSData dataWithContentsOfFile:file];
        
        if (data) { // 直接利用沙盒中圖片
            UIImage *image = [UIImage imageWithData:data];
            cell.imageView.image = image;
            // 存到字典中
            self.images[app.icon] = image;
        } else { // 下載圖片
            cell.imageView.image = [UIImage imageNamed:@"placeholder"];
            
            NSOperation *operation = self.operations[app.icon];
            if (operation == nil) { // 這張圖片暫時沒有下載任務
                operation = [NSBlockOperation blockOperationWithBlock:^{
                    // 下載圖片
                    NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:app.icon]];
                    // 數據加載失敗
                    if (data == nil) {
                        // 移除操作
                        [self.operations removeObjectForKey:app.icon];
                        return;
                    }
                    
                    UIImage *image = [UIImage imageWithData:data];
                    
                    // 存到字典中
                    self.images[app.icon] = image;
                    
                    // 回到主線程顯示圖片
                    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                        [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
                    }];
                    
                    // 將圖片文件數據寫入沙盒中
                    [data writeToFile:file atomically:YES];
                    // 移除操作
                    [self.operations removeObjectForKey:app.icon];
                }];
                
                // 添加到隊列中
                [self.queue addOperation:operation];
                
                // 存放到字典中
                self.operations[app.icon] = operation;
            }
        }
}





// 對比一下三方庫
[cell.imageView sd_setImageWithURL:[NSURL URLWithString:app.icon] placeholderImage:[UIImage imageNamed:@"placeholder"] options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize) {
        NSLog(@"下載進度(已經接收的圖片字節數/圖片的總字節數):%f", 1.0 * receivedSize / expectedSize);
} completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
        NSLog(@"下載完圖片");
}];

NSOperationQueue

添加一段待執行的代碼到NSOperationQueue操作隊列之中,有兩種辦法,其一就是通過一個NSOperation子類的操作對象將待執行的代碼封裝起來,然后通過NSOperationQueue操作隊列的對象調用addOperation這個方法將已經封裝代碼的操作對象添加到隊列之中,這時候NSOperationQueue操作隊列里面的操作對象就會自動調用start方法開始執行封裝的執行代碼,還有就是干脆不實例化操作對象了了,直接通過NSOperationQueue操作隊列調用addOperationWithBlock方法將想要執行的代碼寫到Block之中,這雖然很省力,但是不免要問,這有什么意義呢,我干嘛要在.m文件的方法實現里面實例化一個NSOperationQueue操作隊列然后在執行寫在這個對象的Block參數的代碼塊里面的方法呢,直接把待執行的代碼寫到.m文件多好,根本看不到用NSOperationQueue操作隊列對象的意義呀!哈哈,剛才整理完了demo,發現通過NSOperationQueue操作隊列對象的addOperationWithBlock方法來來添加任務,簡直特別??,因為系統直接就默認這段代碼在子線程中執行了,還有比這更幸福的事情么,我反正想不到開辟一個分線程執行任務更簡單的方法了,暫時真的想不到比這還簡單的方法呀,當然了,我還有一個巨大的收獲,我曾經以為圖片下載有多么多么高大上呢,沒想到本質上就是把那句卡主線的圖片下載代碼當成一個任務放到子線程去執行呀,簡直不要太簡單,好不好?還有一個一意外的收獲就是弄懂了沙盒硬盤緩存和在應用中聲明字典做內存緩存的區別,而且更讓人驚喜的是,無論是硬盤沙盒的讀取操作還是在程序運行時開辟一個字典內存空間存儲圖片都是可以在子線程進行的,簡直不要太炫酷好不好?

操作隊列設置最大并發數

NSOperationQueue的對象調用setMaxConcurrentOperationCount方法,來設置操作隊列最多允許同時執行多少個操作對象,一個操作對象開始執行就表示這個操作對列為這個操作對象開辟了一條新線程,然后這個操作對象就開始調用start方法,將封裝在里面的代碼放在為此操作對象開辟的線程中執行。

操作隊列設置狀態

調用cancelAllOperations方法取消操作隊列里面的所有操作,調用setSuspended方法暫停和恢復操作隊列里面所有的操作對象。也可以針對操作隊列里面的某一個操作對象,然后調用cancel方法取消特定操作對象里面封裝的代碼。

操作隊列管理操作與操作之間的依賴

一說到操作與操作之間的依賴,這個用處就很廣泛了,典型的就是我的直播間的邏輯,要執行很多操作,這些待執行的操作是有嚴格的順序要求的,其實我更多的就是通過Block來實現呀,也可以考慮換一個思路嘛,比如通過操作隊列來設置操作與操作之間的依賴關系。 NSOperation之間可以設置依賴來保證執行順序比如一定要讓操作A執行完后,才能執行操作B,可以這么寫[operationB addDependency:operationA];表示操作B必須在操作A完成結束之后再執行。最激動人心的是,就算A操作對象在隊列1,B操作對象在隊列2,也是可以實現操作A在操作B執行完畢之后再執行。就是這么任性。但是切記切記,不要相互依賴呀,你不要整個A依賴B,又整個B依賴A,你這么做可就讓程序為難了。

SDWebImage工作原理

最占內存資源的還是圖片緩存,問題的關鍵就在我們如何使用SDWebImage庫來緩存圖片。方法就是在調用sd_setImageWithURL之后再加上一個有options選項的方法,SDWebImage默認的其它方法都是默認綜合存儲,也就是內存緩存和磁盤緩存相結合的方式,如果只需要內存緩存,那么在options選項后選擇SDWebImageCacheMemoryOnly就行了。

當調用sd_setImageWithURL方法之后首先將預先放置的本地圖片加載到圖片的位置上,然后SDWebImageManager開始根據URL處理圖片,先從內存的圖片緩存中去查詢是否有圖片,沒有再生成一個從硬盤緩存查找圖片的任務添加到隊列,就算硬盤緩存存在圖片也需要先將圖片添加到內存緩存中,當然可能存在內存緩存的空余內存不夠的情況,必須先清空內存緩存然后在將圖片回調到控制器進行展示。當然如果硬盤緩存里也沒有圖片,就必須通過NSURLConnection來下載圖片,下載完成后自然是異步進行圖片解碼。將解碼的圖片回調到需要的地方進行展示。同時將圖片保存到內存緩存和硬盤緩存,需要注意的是,將圖片寫入硬盤緩存也要異步進行,避免拖慢主線程。更重要的是,SDImageCache在初始化的時候會注冊一些消息通知,在內存警告或退到后臺的時候清理內存圖片緩存,應用結束的時候清理過期圖片

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

推薦閱讀更多精彩內容