iOS_SDWebImage框架分析

SDWebImage 支持異步的圖片下載+緩存,提供了 UIImageView+WebCacha 的 category,方便使用。使用SDWebImage首先了解它加載圖片的流程。
  1. 入口 setImageWithURL:placeholderImage:options: 會先把 placeholderImage 顯示,而後 SDWebImageManager 根據 URL 開始處理圖片。
  2. 進入 SDWebImageManager-downloadWithURL:delegate:options:userInfo:,交給 SDImageCache 從緩存查找圖片是否已經下載 queryDiskCacheForKey:delegate:userInfo:.
  3. 先從內存圖片緩存查找是否有圖片,若是內存中已經有圖片緩存,SDImageCacheDelegate 回調 imageCache:didFindImage:forKey:userInfo: 到 SDWebImageManager。
  4. SDWebImageManagerDelegate 回調 webImageManager:didFinishWithImage: 到 UIImageView+WebCache 等前端展現圖片。
  5. 若是內存緩存中沒有,生成 NSInvocationOperation 添加到隊列開始從硬盤查找圖片是否已經緩存。
  6. 根據 URLKey 在硬盤緩存目錄下嘗試讀取圖片文件。這一步是在 NSOperation 進行的操做,因此回主線程進行結果回調 notifyDelegate:。
  7. 若是上一操做從硬盤讀取到了圖片,將圖片添加到內存緩存中(若是空閒內存太小,會先清空內存緩存)。SDImageCacheDelegate 回調 imageCache:didFindImage: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 和 MKAnnotationView+WebCache,方便使用。
  20. SDWebImagePrefetcher 能夠預先下載圖片,方便後續使用
 
SDWebImage的案例1:在tableView中使用SDWebImage
#import <SDWebImage/UIImageView+WebCache.h>

...

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *MyIdentifier = @"MyIdentifier";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];

    if (cell == nil)
    {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                                       reuseIdentifier:MyIdentifier] autorelease];
    }

    // Here we use the new provided sd_setImageWithURL: method to load the web image
    [cell.imageView sd_setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"]
                      placeholderImage:[UIImage imageNamed:@"placeholder.png"]];
  
    cell.textLabel.text = @"My Text";
    return cell;
}

The SDWebImageManager is the class behind the UIImageView+WebCache category. It ties the asynchronous downloader with the image cache store. You can use this class directly to benefit from web image downloading with caching in another context than a UIView (ie: with Cocoa).前端

 NSURL *url = [NSURL URLWithString:@"http://127.0.0.1/userManager/0.png"];
    //SDWebImageManager管理器繼承與UIImageView+WedCache。已異步的方式進行下載圖片到緩衝區。
    SDWebImageManager *manager = [SDWebImageManager sharedManager];
    [manager downloadImageWithURL:url options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize) {
        NSLog(@"%ld,%ld",receivedSize,expectedSize);
    }
    completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
        //爲imageView設置圖片爲下載的圖片
        [self.imageView setImage:image];
        //打印圖片的來源
        NSLog(@"%@",imageURL);
    }];

It's also possible to use the async image downloader independently:web

[SDWebImageDownloader.sharedDownloader downloadImageWithURL:imageURL
                                                    options:0
                                                   progress:^(NSInteger receivedSize, NSInteger expectedSize)
                                                   {
                                                       // progression tracking code
                                                   }
                                                   completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished)
                                                   {
                                                       if (image && finished)
                                                       {
                                                           // do something with image
                                                       }
                                                   }];

 在本身的案例中使用SDWebImage代碼段:緩存

添加自定義的只讀緩存路徑,其中這是可選的,若是沒有指定,系統會默認
#import "SDWebImage/SDImageCache.h"
#import "AppDelegate.h"
......
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//添加自定義的只讀緩存路徑:可選的
    NSString *bundledPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"CustomPathImages"];
    [[SDImageCache sharedImageCache] addReadOnlyCachePath:bundledPath];
    ......    
}

在使用緩存時,若是內存不夠使用時,須要進行清理以釋放內存。app

#import "MasterViewController.h"
#import "SDWebImage/UIImageView+WebCache.h"
......
//清理緩存,
- (void)flushCache
{
    /**
     *  一、SDImageCache是怎麼作數據管理的?
     SDImageCache分兩個部分,一個是內存層面的,一個是硬盤層面的。
     內存層面的至關是個緩存器,以Key-Value的形式存儲圖片。
     */
    //清理內存緩存
    [SDWebImageManager.sharedManager.imageCache clearMemory];
    //清理硬盤緩存
    [SDWebImageManager.sharedManager.imageCache clearDisk];
}
//支持屏幕旋轉
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
......

//爲了顯示大圖片,替將鏈接中small替換成source;dom

    NSString *largeImageURL = [[_objects objectAtIndex:indexPath.row] stringByReplacingOccurrencesOfString:@"small" withString:@"source"];異步

......async

 

儀表器的使用方法。ide

#import "DetailViewController.h"
#import "SDWebImage/UIImageView+WebCache.h"
......
- (void)configureView
{
    if (self.imageURL) {
        //建立儀表器
        __block UIActivityIndicatorView *activityIndicator;
        //爲防止循環引用問題,因此在block中使用imageView時要設置成弱引用,imageView中使用block,在block中若是再調用imageView時,不設置爲弱引用,會出現循環引用問題。
        __weak UIImageView *weakImageView = self.imageView;
        [self.imageView sd_setImageWithURL:self.imageURL
                          placeholderImage:nil
                                   options:SDWebImageProgressiveDownload
                                  progress:^(NSInteger receivedSize, NSInteger expectedSize) {
                                      //檢測是否已經存在啦!,若是沒有在新建立出一個
                                      if (!activityIndicator) {
                                          //將新建立的儀表器添加到imageView視圖中
                                          [weakImageView addSubview:activityIndicator = [UIActivityIndicatorView.alloc initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]];
                                          activityIndicator.center = weakImageView.center;
                                          //開啓動畫,轉吧!小宇宙
                                          [activityIndicator startAnimating];
                                      }
                                  }
                                 completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL){
                                     //將儀表器從imageView視圖中進行刪除
                                     [activityIndicator removeFromSuperview];
                                     activityIndicator = nil;
                                 }];
    }
}
......

在Cell中使用異步圖片下載。函數

#import "MasterViewController.h"
#import "SDWebImage/UIImageView+WebCache.h"
......
/**
     *  參數解析:
        1.獲取圖片的路徑
        2.設置佔位圖片
        3.options選項:當indexPath.row爲0時,執行 SDWebImageRefreshCached:刷新緩存
     */
    [cell.imageView sd_setImageWithURL:[NSURL URLWithString:[_objects objectAtIndex:indexPath.row]]
                      placeholderImage:[UIImage imageNamed:@"placeholder"] options:indexPath.row == 0 ? SDWebImageRefreshCached : 0];
......

在上段代碼中,有以下兩行代碼: fetch

__weak UIImageView *weakImageView = self.imageView;

__block UIActivityIndicatorView *activityIndicator;

其中第一條爲了不循環引用問題,加上了__weak,第二條使用了block。如今補充一下循環引用、block的知識點:

循環引用
  全部的引用計數系統,都存在循環應用的問題。例以下面的引用關係:
     1)對象a建立並引用到了對象b.
     2)對象b建立並引用到了對象c.
     3)對象c建立並引用到了對象b.
  這時候b和c的引用計數分別是2和1。當a再也不使用b,調用release釋放對b的全部權,由於c還引用了b,因此b的引用計數爲1,

  b不會被釋放。b不釋放,c的引用計數就是1,c也不會被釋放。今後,b和c永遠留在內存中。這種狀況,必須打斷循環引用

  經過其餘規則來維護引用關係。好比,咱們常見的delegate每每是assign方式的屬性而不是retain方式 的屬性,

  賦值不會增長引用計數,就是爲了防止delegation兩端產生沒必要要的循環引用。若是一個UITableViewController對象a

  經過retain獲取了UITableView對象b的全部權,這個UITableView對象b的delegate又是a,若是這個delegate是retain方式的,

  那基本上就沒有機會釋放這兩個對象了。本身在設計使用delegate模式時,也要注意這點。

Block

  是能夠獲取其餘函數局部變量的匿名函數,其不但方便開發,而且能夠大幅提升應用的執行效率(多核心CPU可直接處理Block指令),若是但願進行修改局部變量的值,能夠在定義局部變量以前使用__block。

相關文章
相關標籤/搜索