iOS複習和麪試中有關SDWebImage可能知識點總結(1)

1. SDWebImage內部實現原理步驟

1.1 實現步驟

1. 入口setImageWithUrl:placeHolderImage:options:會把placeHolderImage顯示,而後SDWebImageManager根據URL開始處理圖片.

2. 進入SDWebImageManager-downloadWithURL:delegate:options:userInfo:交給SDImageCache從緩存查找圖片是否已經下載queryDiskCacheForKey:delegate:userInfo:

3. 先從內存圖片緩存查找是否有圖片,若是內存中已經有圖片緩存,SDImageCacheDelegate回調imageCache:didFineImage:forKey:userInfo:到SDWebImageManager.

4. SDWebImageManagerDelegate回調webImageManager:didFinishWithImage:到UIImageView + WebCache等前端展現圖片.

5. 若是內存緩存中沒有,生成NSInvocationOperation添加到隊列開始從硬盤查找圖片是否已經緩存

6. 根據URLKey在硬盤緩存目錄下嘗試讀取圖片文件.這一步是在NSOperation進行的操做,因此回主線程進行結果回調notifyDelegate.

7. 若是上一操做從硬盤讀取到了圖片,將圖片添加到內存緩存中(若是空閒內存太小 會先清空內存緩存).SDImageCacheDelegate 回調imageCache:didFinishImage:forKey:userInfo:進而回調展現圖片.

8. 若是從硬盤緩存目錄讀取不到圖片,說明全部緩存都不存在該圖片,須要下載圖片,回調imageCache:didNotFindImageForKey:userInfo.

9. 共享或從新生成一個下載器SDWebImageDownLoader開始下載圖片

10. 圖片下載由NSURLConnection來作,實現相關delegate來判斷圖片下載中,下載完成和下載失敗

11. connection:didReceiveData:中利用ImageIO作了按圖片下載進度加載效果

12. connectionDidFinishLoading:數據下載完成後交給SDWebImageDecoder作圖片解碼處理

13. 圖片解碼處理在一個NSOperationQueue完成,不會拖慢主線程UI.若是有須要對下載的圖片進行二次處理,最好也在這裏完成,效率會好不少.

14. 在主線程notifyDelegateOnMainThreadWithInfo:宣告解碼完成imageDecoder:didFinishDecodingImage:userInfo:回調給SDWebImageDownloader

15. imageDownLoader:didFinishWithImage:回調給SDWebImageManager告知圖片下載完成

16. 通知全部的downloadDelegates下載完成,回調給須要的地方展現圖片

17. 將圖片保存到SDImageCache中內存緩存和硬盤緩存同時保存,寫文件到硬盤也在以單獨NSInvocationOperation完成,避免拖慢主線程

18. SDImageCache在初始化的時候會註冊一些消息通知,在內存警告或退到後臺的時候清理內存圖片緩存,應用結束的時候清理過時圖片

19. SDWI也提供UIButton + WebCache和MKAnnptation + WebCache方便使用

20. SDWebImagePrefetcher 能夠預先下載圖片,方便後續使用
複製代碼

再用一張圖說明:前端

1.2 API中參數枚舉類型

1.2.1 SDWebImageOptions:圖片下載策略

例如,SD爲UIImageView提供的UIImageView+WebCache.m分類,有這些API:web

- (void)sd_setImageWithURL:(NSURL *)url {
    [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:nil];
}

- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder {
    [self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:nil];
}

- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options {
    [self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:nil];
}

- (void)sd_setImageWithURL:(NSURL *)url completed:(SDWebImageCompletionBlock)completedBlock {
    [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:completedBlock];
}

- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletionBlock)completedBlock {
    [self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:completedBlock];
}

- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletionBlock)completedBlock {
    [self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:completedBlock];
}
複製代碼

其實,以上這些API都直接或間接利用到了SDWebImageOptions這個參數,那麼你記得這個參數有哪些類型?各有什麼做用?瀏覽器

經過查看SDWebImageManager.h源代碼,可知以下:緩存

typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {

    // 默認狀況下,當URL下載失敗時,URL會被列入黑名單,致使庫不會再去重試,該標記用於禁用黑名單
    SDWebImageRetryFailed = 1 << 0,

    // 默認狀況下,圖片下載開始於UI交互,該標記禁用這一特性,這樣下載延遲到UIScrollView減速時
    SDWebImageLowPriority = 1 << 1,

    // 該標記禁用磁盤緩存
    SDWebImageCacheMemoryOnly = 1 << 2,

    // 該標記啓用漸進式下載,圖片在下載過程當中是漸漸顯示的,如同瀏覽器一下。
    // 默認狀況下,圖像在下載完成後一次性顯示
    SDWebImageProgressiveDownload = 1 << 3,

    // 即便圖片緩存了,也指望HTTP響應cache control,並在須要的狀況下從遠程刷新圖片。
    // 磁盤緩存將被NSURLCache處理而不是SDWebImage,由於SDWebImage會致使輕微的性能下載。
    // 該標記幫助處理在相同請求URL後面改變的圖片。若是緩存圖片被刷新,則完成block會使用緩存圖片調用一次
    // 而後再用最終圖片調用一次
    SDWebImageRefreshCached = 1 << 4,

    // 在iOS 4+系統中,當程序進入後臺後繼續下載圖片。這將要求系統給予額外的時間讓請求完成
    // 若是後臺任務超時,則操做被取消
    SDWebImageContinueInBackground = 1 << 5,

    // 經過設置NSMutableURLRequest.HTTPShouldHandleCookies = YES;來處理存儲在NSHTTPCookieStore中的cookie
    SDWebImageHandleCookies = 1 << 6,

    // 容許不受信任的SSL認證
    SDWebImageAllowInvalidSSLCertificates = 1 << 7,

    // 默認狀況下,圖片下載按入隊的順序來執行。該標記將其移到隊列的前面,
    // 以便圖片能當即下載而不是等到當前隊列被加載
    SDWebImageHighPriority = 1 << 8,

    // 默認狀況下,佔位圖片在加載圖片的同時被加載。該標記延遲佔位圖片的加載直到圖片已以被加載完成
    SDWebImageDelayPlaceholder = 1 << 9,

    // 一般咱們不調用動畫圖片的transformDownloadedImage代理方法,由於大多數轉換代碼能夠管理它。
    // 使用這個票房則不任何狀況下都進行轉換。
    SDWebImageTransformAnimatedImage = 1 << 10,
};
複製代碼
1.2.2 SDImageCacheType:圖片緩存策略

例如,設置圖片的兩個例子bash

[self.image2 sd_setImageWithURL:imagePath2 completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
        NSLog(@"這裏能夠在圖片加載完成以後作些事情");
}];
複製代碼
//使用默認圖片,並且用block 在完成後作一些事情
[self.image1 sd_setImageWithURL:imagePath1 placeholderImage:[UIImage imageNamed:@"default"] completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
        NSLog(@"圖片加載完成後作的事情");
}];
複製代碼

還有獲取下載進度的例子cookie

//使用默認圖片,並且用block 在完成後作一些事情
[self.image1 sd_setImageWithURL:imagePath1 placeholderImage:[UIImage imageNamed:@"default"] completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
        NSLog(@"圖片加載完成後作的事情"); 
}];
複製代碼

上面的例子,都有個SDImageCacheTyp的參數,你記得這個參數有哪些?網絡

  • SDImageCacheType
//定義Cache類型
typedef NS_ENUM(NSInteger, SDImageCacheType) {
//不使用cache得到圖片,依然會從web下載圖片
    SDImageCacheTypeNone,
//圖片從disk得到
    SDImageCacheTypeDisk,
//圖片從Memory中得到  
    SDImageCacheTypeMemory
};
複製代碼

2. 最大緩存和時間設置

  • SDImageCache類的源碼
//這個變量默認值爲YES,顯示比較高質量的圖片,可是會浪費比較多的內存,能夠經過設置NO來緩解內存
@property (assign, nonatomic) BOOL shouldDecompressImages;
//總共的內存容許圖片的消耗值
@property (assign, nonatomic) NSUInteger maxMemoryCost;
//圖片存活於內存的時間初始化的時候默認爲一週
@property (assign, nonatomic) NSInteger maxCacheAge;
//每次存儲圖片大小的限制
@property (assign, nonatomic) NSUInteger maxCacheSize;

複製代碼
  • 設置maxCacheSize的例子
SDWebImageManager *manager = [SDWebImageManager sharedManager];
[manager.imageCache setMaxMemoryCost:1000000];//設置總緩存大小,默認爲0沒有限制
[manager.imageCache setMaxCacheSize:640000];//設置單個圖片限制大小
[manager.imageDownloader setMaxConcurrentDownloads:1];//設置同時下載線程數,這是下載器的內容,下面將會介紹
[manager downloadImageWithURL:[NSURL URLWithString:@"http://p9.qhimg.com/t01eb74a44c2eb43193.jpg"]
                      options:SDWebImageProgressiveDownload progress:^(NSInteger receivedSize, NSInteger expectedSize) {
                          NSLog(@"%lu", receivedSize);
                      } completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
                          self.imageView1.image = image;
                          
                      }];
[manager downloadImageWithURL:[NSURL URLWithString:@"http://img.article.pchome.net/00/28/33/87/pic_lib/wm/kuanpin12.jpg"]
                      options:SDWebImageProgressiveDownload progress:^(NSInteger receivedSize, NSInteger expectedSize) {
                          NSLog(@"%lu", receivedSize);
                      } completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
                          self.imageView2.image = image;
                          
                      }];
NSUInteger size = [manager.imageCache getSize];
NSUInteger count = [manager.imageCache getDiskCount];
NSLog(@"size = %lu", size); // 644621(兩張測試圖片)
NSLog(@"count = %lu", count); // 2
[manager.imageCache clearDisk];
size = [manager.imageCache getSize];
count = [manager.imageCache getDiskCount];
NSLog(@"sizeClean = %lu", size);  //  0
NSLog(@"countClean = %lu", count);     //  0   這裏使用的是clear
複製代碼

3. 區分:三種種緩存(內存圖片緩存,磁盤圖片緩存,內存操做緩存)

  • 先查看內存圖片緩存,內存圖片緩存沒有,後生成操做,查看磁盤圖片緩存
  • 磁盤圖片緩存有,就加載到內存緩存,沒有就下載圖片
  • 在創建下載操做以前,判斷下載操做是否存在
  • 默認狀況下,下載的圖片數據會同時緩存到內存和磁盤中

關於緩存位置app

  • 內存緩存是經過 NSCache的子類AutoPurgeCache來實現的;
  • 磁盤緩存是經過 NSFileManager 來實現文件的存儲(默認路徑爲/Library/Caches/default/com.hackemist.SDWebImageCache.default),是異步實現的。

關於圖片下載操做異步

SDWebImage的大部分工做是由緩存對象SDImageCache和異步下載器管理對象SDWebImageManager來完成的。oop

SDWebImage的圖片下載是由SDWebImageDownloader這個類來實現的,它是一個異步下載管理器,下載過程當中增長了對圖片加載作了優化的處理。而真正實現圖片下載的是自定義的一個Operation操做,將該操做加入到下載管理器的操做隊列downloadQueue中,Operation操做依賴系統提供的NSURLConnection類實現圖片的下載。

4. 高清和低清圖片與網絡環境的問題

  • 網絡判斷的問題--利用AFNetworking的API 首先,啓用監控
// AppDelegate.m 文件中
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
{
    // 監控網絡狀態
    [[AFNetworkReachabilityManager sharedManager] startMonitoring];
}
複製代碼

而後,在須要的地方獲取監控管理

// 如下代碼在須要監聽網絡狀態的方法中使用
    AFNetworkReachabilityManager *mgr = [AFNetworkReachabilityManager sharedManager];
    if (mgr.isReachableViaWiFi)     { // 在使用Wifi, 下載原圖
    
    } else     { // 其餘,下載小圖
        
    }
複製代碼
  • 並不能簡單的這樣:WIFI就下載高清圖,蜂窩網絡就下載縮略圖。要考慮和利用緩存的因素。

  • 一個典型例子

- setItem:(CustomItem *)item
{
    _item = item;
    
     // 佔位圖片
    UIImage *placeholder = [UIImage imageNamed:@"placeholderImage"];
    
    // 從內存\沙盒緩存中得到原圖,
    UIImage *originalImage = [[SDImageCache sharedImageCache] imageFromDiskCacheForKey:item.originalImage];
    if (originalImage) { // 若是內存\沙盒緩存有原圖,那麼就直接顯示原圖(無論如今是什麼網絡狀態)
        self.imageView.image = originalImage;
    } else { // 內存\沙盒緩存沒有原圖
        AFNetworkReachabilityManager *mgr = [AFNetworkReachabilityManager sharedManager];
        if (mgr.isReachableViaWiFi) { // 在使用Wifi, 下載原圖
            [self.imageView sd_setImageWithURL:[NSURL URLWithString:item.originalImage] placeholderImage:placeholder];
        } else if (mgr.isReachableViaWWAN) { // 在使用手機自帶網絡
            //     用戶的配置項假設利用NSUserDefaults存儲到了沙盒中
            //    [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"alwaysDownloadOriginalImage"];
            //    [[NSUserDefaults standardUserDefaults] synchronize];
#warning 從沙盒中讀取用戶的配置項:在3G\4G環境是否仍然下載原圖
            BOOL alwaysDownloadOriginalImage = [[NSUserDefaults standardUserDefaults] boolForKey:@"alwaysDownloadOriginalImage"];
            if (alwaysDownloadOriginalImage) { // 下載原圖
                [self.imageView sd_setImageWithURL:[NSURL URLWithString:item.originalImage] placeholderImage:placeholder];
            } else { // 下載小圖
                [self.imageView sd_setImageWithURL:[NSURL URLWithString:item.thumbnailImage] placeholderImage:placeholder];
            }
        } else { // 沒有網絡
            UIImage *thumbnailImage = [[SDImageCache sharedImageCache] imageFromDiskCacheForKey:item.thumbnailImage];
            if (thumbnailImage) { // 內存\沙盒緩存中有小圖
                self.imageView.image = thumbnailImage;
            } else { // 處理離線狀態,並且有沒有緩存時的狀況
                self.imageView.image = placeholder;
            }
        }
    }
}
複製代碼

5. 可能的問題

5.1 後臺沒有處理的高清大圖致使APP內存過大而奔潰?

  • 思路,改寫sd_imageWithData方法的源代碼,可參考https://blog.csdn.net/benyoulai5/article/details/50462586

5.2 SD怎樣解決Cell移出屏幕後的cell中的UIImageView繼續下載問題?-- 移除UIImageView當前綁定的操做。

  • SD爲設置UIImageView提供的API,歸根結底調用的是下面API:
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock {
複製代碼
  • 它的方法實現體中,第一句話就是[self sd_cancelCurrentImageLoad];
  • 這個很是關鍵,當在TableView的cell包含了的UIImageView被重用時,首先調用這一行代碼,保證這個ImageView的下載和緩存組合操做都被取消。若是: ①上次賦值的圖片正在下載,則下載再也不進行; ②下載完成了,但還沒有執行到調用回調(回調包含wself.image = image),因爲操做被取消,於是不會顯示和重用的cell相同的圖片; ③以上兩種狀況只有在網速極慢和手機處理速度極慢的狀況下才會發生,實際上發生的機率很是小,大多數是這種狀況:操做已經進行到下載完成了,此次使用的cell是一個重用的cell,並且保留着imageView的image,對於這種狀況SD會在該實現方法裏面接着設置佔位圖的語句,將image暫時設置爲佔位圖,若是佔位圖爲空,就意味着先暫時清空image。

  • SD內部移除綁定的操做的調用棧爲:
- (void)sd_cancelCurrentImageLoad {
    [self sd_cancelImageLoadOperationWithKey:@"UIImageViewImageLoad"];
}
複製代碼
- (void)sd_cancelImageLoadOperationWithKey:(NSString *)key {
    // Cancel in progress downloader from queue
    NSMutableDictionary *operationDictionary = [self operationDictionary];
    id operations = [operationDictionary objectForKey:key];
    if (operations) {
        if ([operations isKindOfClass:[NSArray class]]) {
            for (id <SDWebImageOperation> operation in operations) {
                if (operation) {
                    [operation cancel];
                }
            }
        } else if ([operations conformsToProtocol:@protocol(SDWebImageOperation)]){
            [(id<SDWebImageOperation>) operations cancel];
        }
        [operationDictionary removeObjectForKey:key];
    }
}
複製代碼

5.3 SDWebImage怎麼實現屏幕滑動時,暫停數據源的任務?經過NSOperationQueue的setSuspend嗎?

3.1 基於NSURLConnection的SDWebImage

(至少2014年7月的版本)老版本的基於 NSURLConnectionSDWebImage 是經過這樣的機制:NSURLConnection工做在主線程,雖然NSURLConnection工做在子線程,但由於UI相關的操做和回調中的setImage都在同一個主線程,滑動屏幕會致使主線程的runloop切換mode爲UITrackingRunLoopMode,此時原來kCFRunLoopDefaultMode上的source (這裏是NSURLConnectionsetImage) 都被暫停,runloop執行不到回調。

它的本意是不讓網絡相關的操做阻塞到主線程,改正:網絡相關的操做在子線程,主線程runloop的mode切換並不會影響子線程,可是它這樣設計的確有這樣的效果:屏幕滑動時,暫停數據下載的任務,改正:滑動屏幕並不會暫停數據下載,暫停的是同一個主線程的setImage

在老版本中,在SDWebImageDownloaderOperation.m文件中有這樣一段話:

SDWebImageDownloaderOperation.m

3.2 基於NSURLSession的SDWebImage

然而,新版本的 SDWebImage 是基於 NSURLSession 的,這個NSURLSession不一樣於NSURLConnection的最大區別是否是基於主線程 子線程 的runloop控制的,而是經過NSOperation新開子線程,因此贊成主線程的runloop切換mode並不會影響子線程的操做。因此,新版本的SDWebImage是沒有這個「滑動即暫停」的效果的。改正:一樣,滑動屏幕並不會暫停數據下載,暫停的是同一個主線程的setImage

若是,實在有須要,有兩種辦法,能夠本身改寫setImage的方法,在裏面設置工做的mode,同老版的SDWebImage同樣改正:一種是改變setImage的線程或者mode。還有一種辦法,能夠監聽ScrollView的拉拽狀態,當ScrollView的代理方法監聽到被拉拽,就suspend操做。

相關文章
相關標籤/搜索