仿SDWebImage多圖片下載

weixin_34194087發表於2015-11-12

一、思路分析

  • 無緩衝


    831339-91b5f17472d39224.png
    無快取.png
  • 有緩衝:優化

831339-f834073c97e9237c.png
有快取.png

二、實現

  • 1.抽取分類:快速獲取沙盒路徑,因為要實現緩衝嗎
// .h 檔案

#import <Foundation/Foundation.h>

@interface NSString (JP)

// 用於生成檔案在caches目錄中的路徑
- (instancetype)cacheDir;
// 用於生成檔案在document目錄中的路徑
- (instancetype)docDir;
// 用於生成檔案在tmp目錄中的路徑
- (instancetype)tmpDir;

// .m檔案

#import "NSString+JP.h"
@implementation NSString (JP)

- (instancetype)cacheDir
{
    // 1.獲取caches目錄
    NSString *path = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
    // 2.生成絕對路徑
    return [path stringByAppendingPathComponent:[self lastPathComponent]];
}

- (instancetype)docDir
{
    NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    return [path stringByAppendingPathComponent:[self lastPathComponent]];
}

- (instancetype)tmpDir
{
    NSString *path = NSTemporaryDirectory();
    return [path stringByAppendingPathComponent:[self lastPathComponent]];
}
@end
  • 2.實現
#import "ViewController.h"
#import "JPApp.h"
#import "NSString+JP.h"

@interface ViewController ()

@property (nonatomic, strong) NSArray *apps; /**< 應用程式模型陣列資訊 */
@property (nonatomic, strong) NSMutableDictionary *imageCaches; /**< 圖片記憶體快取 */
@property (nonatomic, strong) NSMutableDictionary *operations;  /**< 任務快取 */

@end

@implementation ViewController

- (void)viewDidLoad{
    [super viewDidLoad];
    self.tableView.rowHeight = 150;
}

#pragma mark - UITableViewDatasource

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.apps.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 1.獲取cell
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"app"];
    
    // 2.設定資料
    JPApp *app = self.apps[indexPath.row];
    
    cell.textLabel.text = app.name;
    cell.detailTextLabel.text = [NSString stringWithFormat:@"下載:%@", app.download];
    cell.imageView.image = [UIImage imageNamed:@"abc"];
    
    // 設定圖片
    /*
     存在的問題:
     1.在主執行緒中下載圖片, 可能會阻塞主執行緒
     2.重複下載
     */
    // 1.先從記憶體快取中獲取, 如果沒有才去下載
    UIImage *image = self.imageCaches[app.icon];
    if (image == nil) {
        
        // 2.再從磁碟快取中獲取, 如果沒有才去下載
        NSString *filePath = [app.icon cacheDir];
        __block NSData *data = [NSData dataWithContentsOfFile:filePath];
        
        if (data == nil) {
            NSLog(@"下載圖片");
            /*
             存在的問題:
             1.重複設定
             2.重複下載
             */
            NSOperationQueue *queue = [[NSOperationQueue alloc] init];
            
            // 3.判斷當前圖片是否有任務正在下載
            NSBlockOperation *op = self.operations[app.icon];
            if (op == nil) {
                // 沒有對應的下載任務
                op = [NSBlockOperation blockOperationWithBlock:^{
                    // 開啟子執行緒下載
                    // 記憶體快取中沒有值, 需要下載
                    NSURL *url = [NSURL URLWithString:app.icon];
                    data = [NSData dataWithContentsOfURL:url];
                    if (data == nil) {
                        // 如果下載失敗, 應該將當前圖片對應的下載任務從快取中移除 \
                        以便於下次可以再次嘗試下載
                        [self.operations removeObjectForKey:app.icon];
                        return;
                    }

                    UIImage *image = [UIImage imageWithData:data];
                    // 將下載好的圖片快取到記憶體快取中
                    self.imageCaches[app.icon] = image;
                    
                    // 將下載好的圖片寫入到磁碟
                    [data writeToFile:filePath atomically:YES];
                    
                    // 回到主執行緒更新UI
                    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                        NSLog(@"更新UI");
    //                    cell.imageView.image = image;
                        
                        // 重新整理指定的行
                        // 0 / 4
                        [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
                        
                        // 從快取中將當前圖片對應的下載任務移除
                        [self.operations removeObjectForKey:app.icon];
                    }];
                }];
                
                
                // 先將下載任務儲存到快取中
                self.operations[app.icon] = op;
                
                // 將任務新增到佇列中
                [queue addOperation:op];
            }
            
        }else
        {
            NSLog(@"使用磁碟快取");
            NSData *data = [NSData dataWithContentsOfFile:filePath];
            UIImage *image = [UIImage imageWithData:data];
            
            // 將下載好的圖片快取到記憶體快取中
            self.imageCaches[app.icon] = image;
            
            // 更新UI
            cell.imageView.image = image;
        }
    }else
    {
        NSLog(@"使用記憶體快取");
        // 更新UI
        cell.imageView.image = image;
    }
    
    
    // 3.返回cell
    return cell;
}

// 接收到記憶體警告
- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    
    // 釋放當前不需要使用記憶體
    self.imageCaches = nil;
    self.operations = nil;
    self.apps = nil;
}

#pragma mark - lazy
- (NSArray *)apps
{
    if (!_apps) {
        // 1.從plist中載入陣列
        NSString *path = [[NSBundle mainBundle] pathForResource:@"apps.plist" ofType:nil];
        NSArray *arr = [NSArray arrayWithContentsOfFile:path];
        
        // 2.定義陣列儲存轉換好的模型
        NSMutableArray *models = [NSMutableArray arrayWithCapacity:arr.count];
        
        // 3.遍歷陣列中所有的字典, 將字典轉換為模型
        for (NSDictionary *dict in arr) {
            JPApp *app = [JPApp appWithDict:dict];
            [models addObject:app];
        }
        _apps = [models copy];
    }
    return _apps;
}


- (NSMutableDictionary *)imageCaches
{
    if (!_imageCaches) {
        _imageCaches = [NSMutableDictionary dictionary];
    }
    return _imageCaches;
}

- (NSMutableDictionary *)operations
{
    if (!_operations) {
        _operations = [NSMutableDictionary dictionary];
    }
    return _operations;
}

@end

相關文章