1數據準備, 創建一個appInfo模型 ,在視圖控制器中懶加載數組,實現字典轉模型,加載模型數組web
NSMutableArray *data = [NSMutableArray array];
數組
NSMutableArray *list = [NSMutableArray arrayWithCapacity:data.count];緩存
arrayWithCapacity: 容量,指定數組容量,在實例化數組的同時,準備好容量指定的空間,安全
假如是10,一次性在內存中準備好10個空間,再添加元素的時候就不會再次申請內存,若是增長第十一個元素,會再次開闢十個內存空間,網絡
[NSMutableArray array] 每次添加一個元素臨時申請內存空間app
顯然上面的性能比下面的要高不少異步
將可變數組變成不可變的—>有助於線程安全,外部不能修改佈局
使用代碼塊遍歷數組要比for快性能
2 同步加載圖片的效果 在主線程中直接加載測試
// 同步加載圖像
// 1. 模擬延時
NSLog(@"正在下載 %@", app.name);
[NSThread sleepForTimeInterval:0.5];
// 2. 同步加載網絡圖片
NSURL *url = [NSURL URLWithString:app.icon];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
cell.imageView.image = image;
存在問題:
若是網速慢,會卡爆了!影響用戶體驗
滾動表格,會重複下載圖像,形成用戶經濟上的損失,大量耗費流量
破法 —>異步加載圖片
3異步下載圖像
全局操做隊列
// 全局隊列,統一管理全部下載操做@property (nonatomic, strong) NSOperationQueue *downloadQueue;
懶加載
- (NSOperationQueue *)downloadQueue { if (_downloadQueue == nil) { _downloadQueue = [[NSOperationQueue alloc] init]; } return _downloadQueue; }
異步下載
// 異步加載圖像
// 1. 定義下載操做
// 異步加載圖像
NSBlockOperation *downloadOp = [NSBlockOperation blockOperationWithBlock:^{
// 1. 模擬延時
NSLog(@"正在下載 %@", app.name);
[NSThread sleepForTimeInterval:0.5];
// 2. 異步加載網絡圖片
NSURL *url = [NSURL URLWithString:app.icon];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
// 3. 主線程更新 UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
cell.imageView.image = image;
}];
}];
// 2. 將下載操做添加到隊列
[self.downloadQueue addOperation:downloadOp];
存在的問題
下載完成後,不顯示圖片
緣由:使用的是系統提供的cell
異步方法只是設置了圖像,可是沒有設置frame
圖片加載後,一旦與cell交互,會調用layoutsubviews,從新調整佈局
破法 : 0 使用佔位圖像
1 使用自定義cell
cell.nameLabel.text = app.name;
cell.downloadLabel.text = app.download;
// 異步加載圖像
// 0. 佔位圖像
UIImage *placeholder = [UIImage imageNamed:@"user_default"];
cell.iconView.image = placeholder;
// 1. 定義下載操做
NSBlockOperation *downloadOp = [NSBlockOperation blockOperationWithBlock:^{
// 1. 模擬延時
NSLog(@"正在下載 %@", app.name);
[NSThread sleepForTimeInterval:0.5];
// 2. 異步加載網絡圖片
NSURL *url = [NSURL URLWithString:app.icon];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
// 3. 主線程更新 UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
cell.iconView.image = image;
}];
}];
// 2. 將下載操做添加到隊列
[self.downloadQueue addOperation:downloadOp];
問題: 若是網絡圖片下載速度,不一致,同時用戶滾動圖片,可能顯示圖片錯行問題
修改延時代碼,查看錯誤
// 1. 模擬延時
if (indexPath.row > 9) {
[NSThread sleepForTimeInterval:3.0];
}
上下滾動一下表格便可看到 cell 複用的錯誤
破法 : MVC
image
屬性#import <UIKit/UIKit.h>/// 下載的圖像@property (nonatomic, strong) UIImage *image;
判斷模型中是否已經存在圖像
if (app.image != nil) { NSLog(@"加載模型圖像..."); cell.iconView.image = app.image; return cell; }
下載完成後設置模型圖像
// 3. 主線程更新 UI[[NSOperationQueue mainQueue] addOperationWithBlock:^{ // 設置模型中的圖像 app.image = image; // 刷新表格 [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; }];
問題
若是圖像下載很慢,用戶滾動表格很快,會形成重複建立下載操做
修改延時代碼
// 1. 模擬延時if (indexPath.row == 0) { [NSThread sleepForTimeInterval:10.0]; }
快速滾動表格,將第一行不斷「滾出/滾入」界面能夠查看操做被重複建立的問題
操做緩衝池
@property (nonatomic, strong) NSMutableDictionary *operationCache;
- (NSMutableDictionary *)operationCache { if (_operationCache == nil) {
_operationCache = [NSMutableDictionary dictionary];
} return _operationCache;
}
// 異步加載圖像// 0. 佔位圖像UIImage *placeholder = [UIImage imageNamed:@"user_default"];
cell.iconView.image = placeholder;// 判斷操做是否存在if (self.operationCache[app.icon] != nil) { NSLog(@"正在玩命下載中..."); return cell;
}
// 2. 將操做添加到操做緩衝池[self.operationCache setObject:downloadOp forKey:app.icon];// 3. 將下載操做添加到隊列[self.downloadQueue addOperation:downloadOp];
[self.operationCache removeObjectForKey:app.icon];
__weak typeof(self) weakSelf = self;
- (void)dealloc { NSLog(@"我去了");
}
/// 圖像緩衝池@property (nonatomic, strong) NSMutableDictionary *imageCache;
- (NSMutableDictionary *)imageCache { if (_imageCache == nil) {
_imageCache = [[NSMutableDictionary alloc] init];
} return _imageCache;
}
- (void)downloadImage:(NSIndexPath *)indexPath { // 1. 根據 indexPath 獲取數據模型
AppInfo *app = self.appList[indexPath.row]; // 2. 判斷操做是否存在
if (self.operationCache[app.icon] != nil) { NSLog(@"正在玩命下載中..."); return;
} // 3. 定義下載操做
__weak typeof(self) weakSelf = self;
NSBlockOperation *downloadOp = [NSBlockOperation blockOperationWithBlock:^{ // 1. 模擬延時
NSLog(@"正在下載 %@", app.name); if (indexPath.row == 0) {
[NSThread sleepForTimeInterval:3.0];
} // 2. 異步加載網絡圖片
NSURL *url = [NSURL URLWithString:app.icon]; NSData *data = [NSData dataWithContentsOfURL:url]; UIImage *image = [UIImage imageWithData:data]; // 3. 主線程更新 UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{ // 將下載操做從緩衝池中刪除
[weakSelf.operationCache removeObjectForKey:app.icon]; if (image != nil) { // 設置模型中的圖像
[weakSelf.imageCache setObject:image forKey:app.icon]; // 刷新表格
[weakSelf.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}
}];
}]; // 4. 將操做添加到操做緩衝池
[self.operationCache setObject:downloadOp forKey:app.icon]; // 5. 將下載操做添加到隊列
[self.downloadQueue addOperation:downloadOp];
}
若是接收到內存警告,程序必定要作處理,工做中的程序必定要處理,不然後果很嚴重!!!
若是網絡正常,可是圖像下載失敗後,爲了不再次都從網絡上下載該圖像,可使用「黑名單」
@property (nonatomic, strong) NSMutableArray *blackList;
- (NSMutableArray *)blackList { if (_blackList == nil) {
_blackList = [NSMutableArray array];
} return _blackList;
}
if (image != nil) {
// 設置模型中的圖像
[weakSelf.imageCache setObject:image forKey:app.icon];
// 刷新表格
[weakSelf.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
} else {
// 下載失敗記錄在黑名單中
[weakSelf.blackList addObject:app.icon];
}