多圖下載代碼

  • LZAppItem模型類
// LZAppItem.h
#import <Foundation/Foundation.h>

@interface LZAppItem : NSObject

/** 名稱*/
@property (nonatomic ,strong) NSString *name;
/** 圖標(biāo)的地址*/
@property (nonatomic ,strong) NSString *icon;
/** 下載量*/
@property (nonatomic ,strong) NSString *download;

+(instancetype)appItemWithDict:(NSDictionary *)dict;

@end

// LZAppItem.m
#import "LZAppItem.h"

@implementation LZAppItem

+ (instancetype)appItemWithDict:(NSDictionary *)dict
{
    // 創(chuàng)建對象
    LZAppItem *appItem = [[LZAppItem alloc] init];
    // KVC
    [appItem setValuesForKeysWithDictionary:dict];
    // 返回對象
    return appItem;
}

@end
  • ViewController
// ViewController.h
#import <UIKit/UIKit.h>

@interface ViewController : UITableViewController


@end

// ViewController.m
#import "ViewController.h"
#import "LZAppItem.h"

@interface ViewController ()
/** 隊列*/
@property (nonatomic ,strong) NSOperationQueue *queue;
/** 模型數(shù)組*/
@property (nonatomic, strong) NSArray *apps;
/** 圖片緩存*/
@property (nonatomic ,strong) NSMutableDictionary *images;
/** 操作*/
@property (nonatomic ,strong) NSMutableDictionary *operations;
@end

@implementation ViewController

#pragma mark - 懶加載數(shù)據(jù)
- (NSArray *)apps
{
    if (_apps == nil) {
        // 1獲取字典數(shù)組
        // 1.1獲取路徑
        NSString *filePath = [[NSBundle mainBundle] pathForResource:@"apps.plist" ofType:nil];
        // 1.2獲取到字典數(shù)組
        NSArray *dictArray = [NSArray arrayWithContentsOfFile:filePath];
        // 2.拿到模型數(shù)組
        // 2.1創(chuàng)建一個可變數(shù)組
        NSMutableArray *temp = [NSMutableArray array];
        // 2.2創(chuàng)建for循環(huán)
        for (NSDictionary *dict in dictArray) {
            LZAppItem *item = [LZAppItem appItemWithDict:dict];
            [temp addObject:item];
        }
        _apps = temp;
    }
    return _apps;
}

#pragma mark - 圖片緩存
- (NSMutableDictionary *)images
{
    if (_images == nil) {
        _images = [NSMutableDictionary dictionary];
    }
    return _images;
}

#pragma mark - 隊列
- (NSOperationQueue *)queue
{
    if (_queue == nil) {
        _queue = [[NSOperationQueue alloc] init];
        _queue.maxConcurrentOperationCount = 3;
    }
    return _queue;
}

#pragma mark - UITableViewDataSource方法
// 返回多少組
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

// 每組返回多少行
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.apps.count;
}

// 每行顯示什么內(nèi)容
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *ID = @"app";
    // 去緩存池里面找
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
    }

    // 拿到模型數(shù)據(jù)
    LZAppItem *item = self.apps[indexPath.row];
    // 賦值
    cell.textLabel.text = item.name;
    cell.detailTextLabel.text = item.download;

    // 賦值圖片,重點

    // 檢查緩存,用圖片的地址做鍵
    UIImage *image = [self.images objectForKey:item.icon];
    if (image) { // 有內(nèi)存緩存,即身上有錢
        // 直接賦值
        cell.imageView.image = image;
//        NSLog(@"%zd使用了內(nèi)存緩存",indexPath.row);
    }else { // 沒有內(nèi)存緩存,即身上沒有錢

        // 獲取磁盤緩存路徑
        // 0.0獲取路徑
        NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
        // 0.1得到圖片的名稱
        NSString *fileName = [item.icon lastPathComponent];
        // 0.2拼接文件的路徑
        NSString *fullPath = [cachePath stringByAppendingPathComponent:fileName];

        // 去檢查磁盤緩存,這里找到的是保存在cache里面的NSData數(shù)據(jù)
        NSData *imgData = [NSData dataWithContentsOfFile:fullPath];

        if (imgData) { // 如果磁盤緩存里面有該圖片,磁盤緩存即是卡里面有錢
            // 拿到Image
            UIImage *image = [UIImage imageWithData:imgData];
            cell.imageView.image = image;

            // 把圖片保存到身上,內(nèi)存緩存
            [self.images setObject:image forKey:item.icon];

//            NSLog(@"%zd使用了磁盤緩存",indexPath.row);

        } else { // 如果磁盤緩存里面沒有該圖片,磁盤緩存即是卡里面沒有錢
            // 來到這里,說明又沒有內(nèi)存緩存,又沒有磁盤緩存
            // 清空圖片或者是設(shè)置占位圖片,目的是什么,因為cell是重復(fù)利用的,假設(shè)當(dāng)你第一張圖片顯示完畢的時候,用戶繼續(xù)往下拖拽,下面的cell是由上面消失的cell重復(fù)利用過來的,而下面的cell去下載圖片的時間可能比較長,所以顯示的效果是上一張殘留下來的圖片,之后再把從網(wǎng)絡(luò)下載的圖片進(jìn)行覆蓋,也就是圖片錯亂了,所以,為了防止這個問題,用一張占位圖片解決
            cell.imageView.image = [UIImage imageNamed:@"Snip20200808_172"];

            /*避免重復(fù)操作,當(dāng)程序第一次運行起來的時候,顯示第一個cell的時候,創(chuàng)建一個操作去服務(wù)器端獲取數(shù)據(jù),然后用戶又隨便往下拖拽,那么第一個cell就不再顯示了,此時那個獲取數(shù)據(jù)的操作還在執(zhí)行,假設(shè)需要10秒,然后用戶又隨便拖拽,滾到了第一個cell,第一個cell又重新顯示出來了,那么它還會繼續(xù)創(chuàng)建一個操作去服務(wù)器端獲取數(shù)據(jù),那么可能就有好幾個操作發(fā)送到服務(wù)器端去獲取同一個數(shù)據(jù)了,沒有必要,所以,這里采用了一個可變字典,用來判斷,好辦法,誰想出來的,牛逼*/
            // 檢查操作緩存
            NSBlockOperation *dowbloadOperation = [self.operations objectForKey:item.icon];
            if (dowbloadOperation) { // 如果有操作,說明之前已經(jīng)發(fā)了一次操作過去了,那么再次來到這的時候,就不能再發(fā)請求了,所以什么也不能做

            } else { // 如果沒有操作,說明之前沒有發(fā)過操作過去,要添加操作
                    dowbloadOperation = [NSBlockOperation blockOperationWithBlock:^{


                    // 1.創(chuàng)建url
                    NSURL *url = [NSURL URLWithString:item.icon];
                    // 2.拿到二進(jìn)制數(shù)據(jù)
                    // 該方法通過url獲取數(shù)據(jù)是有時間限制的,30秒,如果失敗,返回nil
                    NSData *data = [NSData dataWithContentsOfURL:url];
                    // 3.轉(zhuǎn)化為UIImage對象
                    UIImage *image = [UIImage imageWithData:data];
                    // 4.判斷
                    if (image == nil) {
                        // 把這次操作刪除掉
                        [self.operations removeObjectForKey:item.icon];
                        return ;
                    }
                    // 5.保存到內(nèi)存緩存
                    [self.images setValue:image forKey:item.icon];
                    // 6.保存到磁盤緩存
                    [data writeToFile:fullPath atomically:YES];

                    NSLog(@"%zd直接下載",indexPath.row);

                    // 設(shè)置圖片
                   // 下面這行代碼應(yīng)該放在主線程,那么應(yīng)該回到主線程
                    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                        // 下面這個block塊里面的代碼是在主線程執(zhí)行的
                        // 如果沒有刷新操作,那么不會顯示圖片,為什么,因為你雖然賦值了,但是沒有
                        // 調(diào)用它的layoutsubviews創(chuàng)建尺寸,只有調(diào)用layoutsubviews才會顯示出圖片
                        // 也就是刷新,或者點擊某一行的時候,它會觸發(fā)layoutsubviews方法,那么才會
                        // 顯示出圖片了,但是,你用reloadData又沒有必要,只需要設(shè)置某一張圖片,你刷新
                        // 整個可視區(qū)域,那就傻逼了,所以,這里面最好的方式是,刷新特定的行數(shù)
//                        cell.imageView.image = image;
//                        [tableView reloadData];
                        [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationBottom];
                    }];

                }];

                // 添加操作到內(nèi)存緩存中
                [self.operations setObject:dowbloadOperation forKey:item.icon];

                // 添加到隊列
                [self.queue addOperation:dowbloadOperation];
            }

        }
    }

    // 返回cell
    return cell;
}

- (void)didReceiveMemoryWarning
{
    // 移除內(nèi)存緩存
    [self.images removeAllObjects];
    // 取消隊列中的操作
    [self.queue cancelAllOperations];
}
@end

  • 效果圖片:
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容