一、NSOperation VS GCD
- GCD
- GCD是iOS4.0 推出的,主要針對多核cpu做了優化,是C語言的技術
- GCD是將任務(block)添加到佇列(串行/并行/全域/主佇列),并且以同步/異步的方式執行任務的函式
- GCD提供了一些NSOperation不具備的功能
- 一次性執行
- 延遲執行
- 調度組
- NSOperation
- NSOperation是iOS2.0推出的,iOS4之后重寫了NSOperation
- NSOperation將操作(異步的任務)添加到佇列(并發佇列),就會執行指定操作的函式
- NSOperation里提供的方便的操作
- 最大并發數
- 佇列的暫定/繼續
- 取消所有的操作
- 指定操作之間的依賴關系(GCD可以用同步實作)
二、最大并發數
- 什么是并發數
同時執行的任務數
比如,同時開3個執行緒執行3個任務,并發數就是3
- 最大并發數的相關方法
- (NSInteger)maxConcurrentOperationCount;
- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
- 執行的程序
- 1、把操作添加到佇列self.queue addOperationWithBlock
- 2、去執行緒池去取空閑的執行緒,如果沒有就創建執行緒
- 3、把操作交給從執行緒池中取出的執行緒執行
- 4、執行完成后,把執行緒再放回執行緒池中
- 5、重復2,3,4知道所有的操作都執行完
佇列的暫停、取消、恢復
取消佇列的所有操作
- (void)cancelAllOperations;
提示:也可以呼叫NSOperation的- (void)cancel方法取消單個操作
暫停和恢復佇列
- (void)setSuspended:(BOOL)b; // YES代表暫停佇列,NO代表恢復佇列
- (BOOL)isSuspended;
搖獎機
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UILabel *lbl1;
@property (weak, nonatomic) IBOutlet UILabel *lbl2;
@property (weak, nonatomic) IBOutlet UILabel *lbl3;
@property (weak, nonatomic) IBOutlet UIButton *startButton;
//全域佇列
@property (nonatomic, strong) NSOperationQueue *queue;
@end
@implementation ViewController
//懶加載
- (NSOperationQueue *)queue {
if (_queue == nil) {
_queue = [NSOperationQueue new];
}
return _queue;
}
//點擊開始執行
- (IBAction)start:(UIButton *)sender {
//當佇列中有操作的時候,不添加操作
if (self.queue.operationCount == 0) {
//異步執行 添加操作
[self.queue addOperationWithBlock:^{
[self random];
}];
[self.startButton setTitle:@"暫停" forState:UIControlStateNormal];
self.queue.suspended = NO;
}else if(!self.queue.isSuspended) {
//正在執行的時候,暫停
//先把當前的操作執行完畢,暫停后續的操作
self.queue.suspended = YES;
[self.startButton setTitle:@"繼續" forState:UIControlStateNormal];
}
}
//隨機生成3個數字,顯示到label上
- (void)random {
while (!self.queue.isSuspended) {
[NSThread sleepForTimeInterval:0.05];
//生成亂數 [0,10) 0-9
int num1 = arc4random_uniform(10);
int num2 = arc4random_uniform(10);
int num3 = arc4random_uniform(10);
//回到主執行緒上更新UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
//給label賦值
self.lbl1.text = [NSString stringWithFormat:@"%d",num1];
self.lbl2.text = [NSString stringWithFormat:@"%d",num2];
self.lbl3.text = [NSString stringWithFormat:@"%d",num3];
}];
}
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSLog(@"%zd",self.queue.operationCount);
}
三、操作的優先級
- 設定NSOperation在queue中的優先級,可以改變操作的執行優先級
- (NSOperationQueuePriority)queuePriority;
- (void)setQueuePriority:(NSOperationQueuePriority)p;
- iOS8以后推薦使用服務質量 qualityOfService
監聽操作完成
- 可以監聽一個操作的執行完畢
- (void (^)(void))completionBlock;
- (void)setCompletionBlock:(void (^)(void))block;
@interface ViewController ()
@property (nonatomic, strong) NSOperationQueue *queue;
@end
@implementation ViewController
//懶加載
- (NSOperationQueue *)queue{
if (_queue == nil) {
_queue = [NSOperationQueue new];
}
return _queue;
}
- (void)viewDidLoad {
[super viewDidLoad];
//操作1
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 20; i++) {
NSLog(@"op1 %d",i);
}
}];
//設定優先級最高
op1.qualityOfService = NSQualityOfServiceUserInteractive;
[self.queue addOperation:op1];
//等操作完成,執行 執行在子執行緒上
[op1 setCompletionBlock:^{
NSLog(@"============op1 執行完成========== %@",[NSThread currentThread]);
}];
//操作2
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 20; i++) {
NSLog(@"op2 %d",i);
}
}];
//設定優先級最低
op2.qualityOfService = NSQualityOfServiceBackground;
[self.queue addOperation:op2];
}
@end
四、操作依賴
- NSOperation之間可以設定依賴來保證執行順序
比如一定要讓操作A執行完后,才能執行操作B,可以這么寫
[operationB addDependency:operationA]; // 操作B依賴于操作A
可以在不同queue的NSOperation之間創建依賴關系

模擬軟體升級程序:下載—解壓—升級完成
@interface ViewController ()
@property (nonatomic, strong) NSOperationQueue *queue;
@end
@implementation ViewController
- (NSOperationQueue *)queue {
if (_queue == nil) {
_queue = [NSOperationQueue new];
}
return _queue;
}
- (void)viewDidLoad {
[super viewDidLoad];
// 下載 - 解壓 - 升級完成
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"下載");
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:2.0];
NSLog(@"解壓");
}];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"升級完成");
}];
//設定操作間的依賴
[op2 addDependency:op1];
[op3 addDependency:op2];
//錯誤,會發生回圈依賴,什么都不執行
// [op1 addDependency:op3];
//操作添加到佇列中
[self.queue addOperations:@[op1,op2] waitUntilFinished:NO];
//依賴關系可以夸佇列執行
[[NSOperationQueue mainQueue] addOperation:op3];
}
案例-UITableView中顯示圖片

步驟1—資料模型準備
- 把準備好的資料源(plist)轉換成我們使用方便的物件集合
從字典型別自定系結屬性
[obj setValuesForKeysWithDictionary:dict];
提供方法—---生成所有物件的一個集合
+ (NSArray *)appList;
搭建界面
- UITabelViewController
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
把name和download都先顯示到界面上
同步方式下載圖片
- 在cell生成的時候下載圖片
//模擬網路延時
[NSThread sleepForTimeInterval:0.5];
NSURL *url = [NSURL URLWithString:appInfo.icon];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *img = [UIImage imageWithData:data];
問題:進入界面很慢,一滑動就卡
原因:同步方式去下載圖片的時候,系統無法很快執行界面渲染,俗稱“卡主執行緒”
問題:每次上拉下拉的時候都會重復下載圖片
ViewController.m
#import "ViewController.h"
#import "HMAppInfo.h"
@interface ViewController ()
@property (nonatomic, strong) NSArray *appInfos;
@end
//1 創建模型類,獲取資料,測驗
//2 資料源方法
//3 同步下載圖片
@implementation ViewController
//懶加載
- (NSArray *)appInfos {
if (_appInfos == nil) {
_appInfos = [HMAppInfo appInfos];
}
return _appInfos;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//1 測驗模型資料
// NSLog(@"%@",self.appInfos);
}
//2 資料源方法
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.appInfos.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//1 創建可重用的cell
static NSString *reuseId = @"appInfo";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseId];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseId];
}
//2 獲取資料,給cell內部子控制元件賦值
HMAppInfo *appInfo = self.appInfos[indexPath.row];
cell.textLabel.text = appInfo.name;
cell.detailTextLabel.text = appInfo.download;
//同步下載圖片
//模擬網速比較慢
[NSThread sleepForTimeInterval:0.5];
NSURL *url = [NSURL URLWithString:appInfo.icon];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *img = [UIImage imageWithData:data];
cell.imageView.image = img;
//3 回傳cell
return cell;
}
@end
HMAppInfo.h
@interface HMAppInfo : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *icon;
@property (nonatomic, copy) NSString *download;
+ (instancetype)appInfoWithDic:(NSDictionary *)dic;
//獲取所有的模型資料
+ (NSArray *)appInfos;
@end
HMAppInfo.m
#import "HMAppInfo.h"
@implementation HMAppInfo
+ (instancetype)appInfoWithDic:(NSDictionary *)dic {
HMAppInfo *appInfo = [[self alloc] init];
//kvc給屬性賦值
[appInfo setValuesForKeysWithDictionary:dic];
return appInfo;
}
+ (NSArray *)appInfos{
//加載plist
NSString *path = [[NSBundle mainBundle] pathForResource:@"apps.plist" ofType:nil];
NSArray *array = [NSArray arrayWithContentsOfFile:path];
NSMutableArray *mArray = [NSMutableArray arrayWithCapacity:10];
//便利陣列的另一種方式
[array enumerateObjectsUsingBlock:^(NSDictionary *obj, NSUInteger idx, BOOL * _Nonnull stop) {
//字典轉模型
HMAppInfo *appInfo = [self appInfoWithDic:obj];
[mArray addObject:appInfo];
}];
//回傳 對可變陣列進行copy操作,變成不可變陣列
return mArray.copy;
}
@end
異步方式下載圖片
- 異步方式下載圖片
//異步加載網路圖片
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
}];
- 問題1:圖片顯示不出,點擊才顯示
- 原因:cell中的imageView是懶加載的,在初始化的時候圖片沒有指定,所以圖片大小為0x0
- 解決:設定占位圖片
ViewController.m
#import "ViewController.h"
#import "HMAppInfo.h"
#import "HMAppInfoCell.h"
@interface ViewController ()
@property (nonatomic, strong) NSArray *appInfos;
//全域佇列
@property (nonatomic, strong) NSOperationQueue *queue;
@end
//1 創建模型類,獲取資料,測驗
//2 資料源方法
//3 同步下載圖片--如果網速比較慢,界面會卡頓
//4 異步下載圖片--圖片顯示不出來,點擊cell或者上下拖動圖片可以顯示
//解決,使用占位圖片
@implementation ViewController
//懶加載
- (NSArray *)appInfos {
if (_appInfos == nil) {
_appInfos = [HMAppInfo appInfos];
}
return _appInfos;
}
- (NSOperationQueue *)queue {
if (_queue == nil) {
_queue = [NSOperationQueue new];
}
return _queue;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//1 測驗模型資料
// NSLog(@"%@",self.appInfos);
}
//2 資料源方法
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.appInfos.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//1 創建可重用的cell
static NSString *reuseId = @"appInfo";
HMAppInfoCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseId];
if (cell == nil) {
cell = [[HMAppInfoCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseId];
}
//2 獲取資料,給cell內部子控制元件賦值
HMAppInfo *appInfo = self.appInfos[indexPath.row];
//cell內部的子控制元件都是懶加載的
//當回傳cell之前,會呼叫cell的layoutSubviews方法
cell.textLabel.text = appInfo.name;
cell.detailTextLabel.text = appInfo.download;
//設定占位圖片
cell.imageView.image = [UIImage imageNamed:@"user_default"];
//異步下載圖片
//模擬網速比較慢
[self.queue addOperationWithBlock:^{
[NSThread sleepForTimeInterval:0.5];
NSURL *url = [NSURL URLWithString:appInfo.icon];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *img = [UIImage imageWithData:data];
//主執行緒上更新UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
cell.imageView.image = img;
}];
}];
//3 回傳cell
return cell;
}
@end
HMAppInfoCell.m
#import "HMAppInfoCell.h"
@implementation HMAppInfoCell
- (void)layoutSubviews {
[super layoutSubviews];
NSLog(@"layoutSubviews");
}
@end
- 問題2:頻繁滾動時,反復下載相同圖片
- 解決:圖片快取,模型類中增加image的屬性
- 問題3:頻繁滾動時,并且超出螢屏的圖片下載速度比較慢的情況,圖片可能錯位,并且圖片來回跳
- 原因:cell的重用
- 解決:當異步下載完成后,回到主執行緒重新加載對應的cell
ViewController.m
#import "ViewController.h"
#import "HMAppInfo.h"
#import "HMAppInfoCell.h"
@interface ViewController ()
@property (nonatomic, strong) NSArray *appInfos;
//全域佇列
@property (nonatomic, strong) NSOperationQueue *queue;
@end
//1 創建模型類,獲取資料,測驗
//2 資料源方法
//3 同步下載圖片--如果網速比較慢,界面會卡頓
//4 異步下載圖片--圖片顯示不出來,點擊cell或者上下拖動圖片可以顯示
//解決,使用占位圖片
//5 圖片快取--把網路上下載的圖片,保存到記憶體
//解決,圖片重復下載,把圖片快取到記憶體中,節省用戶的流量 (拿空間換取執行時間)
//6 解決圖片下載速度特別慢,出現的錯行問題,
//當圖片下載完成之后,重新加載對應的cell
@implementation ViewController
//懶加載
- (NSArray *)appInfos {
if (_appInfos == nil) {
_appInfos = [HMAppInfo appInfos];
}
return _appInfos;
}
- (NSOperationQueue *)queue {
if (_queue == nil) {
_queue = [NSOperationQueue new];
}
return _queue;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//1 測驗模型資料
// NSLog(@"%@",self.appInfos);
}
//2 資料源方法
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.appInfos.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//1 創建可重用的cell
static NSString *reuseId = @"appInfo";
HMAppInfoCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseId];
if (cell == nil) {
cell = [[HMAppInfoCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseId];
}
//2 獲取資料,給cell內部子控制元件賦值
HMAppInfo *appInfo = self.appInfos[indexPath.row];
//cell內部的子控制元件都是懶加載的
//當回傳cell之前,會呼叫cell的layoutSubviews方法
cell.textLabel.text = appInfo.name;
cell.detailTextLabel.text = appInfo.download;
//判斷有沒有圖片快取
if (appInfo.image) {
NSLog(@"從快取加載圖片...");
cell.imageView.image = appInfo.image;
return cell;
}
//設定占位圖片
cell.imageView.image = [UIImage imageNamed:@"user_default"];
//異步下載圖片
//模擬網速比較慢
[self.queue addOperationWithBlock:^{
// [NSThread sleepForTimeInterval:0.5];
//模擬圖片下載速度慢
if (indexPath.row > 9) {
[NSThread sleepForTimeInterval:5];
}
//下載圖片
NSLog(@"下載網路圖片...");
NSURL *url = [NSURL URLWithString:appInfo.icon];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *img = [UIImage imageWithData:data];
//快取圖片
appInfo.image = img;
//主執行緒上更新UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// cell.imageView.image = img;
//解決圖片顯示錯行
[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}];
}];
//3 回傳cell
return cell;
}
@end
操作快取池
- 問題:由于滑動過快導致多次下載相同圖片
- 解決:操作快取
- 定義一個字典,把每次操作都放在里面,避免相同操作的出現
NSMutableDictionary *operationCaches;
@interface ViewController ()
@property (nonatomic, strong) NSArray *appInfos;
//全域佇列
@property (nonatomic, strong) NSOperationQueue *queue;
//圖片的快取池
@property (nonatomic, strong) NSMutableDictionary *imageCache;
//下載操作快取池
@property (nonatomic, strong) NSMutableDictionary *downloadCache;
@end
//1 創建模型類,獲取資料,測驗
//2 資料源方法
//3 同步下載圖片--如果網速比較慢,界面會卡頓
//4 異步下載圖片--圖片顯示不出來,點擊cell或者上下拖動圖片可以顯示
//解決,使用占位圖片
//5 圖片快取--把網路上下載的圖片,保存到記憶體--圖片存盤在模型物件中
//解決,圖片重復下載,把圖片快取到記憶體中,節省用戶的流量 (拿空間換取執行時間)
//6 解決圖片下載速度特別慢,出現的錯行問題,
//當圖片下載完成之后,重新加載對應的cell
//7 當收到記憶體警告,要清理記憶體,如果圖片存盤在模型物件中,不好清理記憶體
//圖片的快取池
//8 當有些圖片下載速度比較慢,上下不停滾動的時候,會重復下載圖片,會浪費流量
//判斷當前是否有對應圖片的下載操作,如果沒有添加下載操作,如果有不要重復創建操作
//下載操作的快取池
@implementation ViewController
//懶加載
- (NSArray *)appInfos {
if (_appInfos == nil) {
_appInfos = [HMAppInfo appInfos];
}
return _appInfos;
}
- (NSOperationQueue *)queue {
if (_queue == nil) {
_queue = [NSOperationQueue new];
}
return _queue;
}
- (NSMutableDictionary *)imageCache {
if (_imageCache == nil) {
_imageCache = [NSMutableDictionary dictionaryWithCapacity:10];
}
return _imageCache;
}
- (NSMutableDictionary *)downloadCache {
if (_downloadCache == nil) {
_downloadCache = [NSMutableDictionary dictionaryWithCapacity:10];
}
return _downloadCache;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//1 測驗模型資料
// NSLog(@"%@",self.appInfos);
}
//2 資料源方法
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.appInfos.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//1 創建可重用的cell
static NSString *reuseId = @"appInfo";
HMAppInfoCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseId];
if (cell == nil) {
cell = [[HMAppInfoCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseId];
}
//2 獲取資料,給cell內部子控制元件賦值
HMAppInfo *appInfo = self.appInfos[indexPath.row];
//cell內部的子控制元件都是懶加載的
//當回傳cell之前,會呼叫cell的layoutSubviews方法
cell.textLabel.text = appInfo.name;
cell.detailTextLabel.text = appInfo.download;
//判斷有沒有圖片快取
if (self.imageCache[appInfo.icon]) {
NSLog(@"從快取加載圖片...");
cell.imageView.image = self.imageCache[appInfo.icon];
return cell;
}
//設定占位圖片
cell.imageView.image = [UIImage imageNamed:@"user_default"];
//判斷下載操作快取池 中是否有對應的操作
if (self.downloadCache[appInfo.icon]) {
NSLog(@"正在拼命下載圖片...");
return cell;
}
//異步下載圖片
//模擬網速比較慢
//下載操作
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
// [NSThread sleepForTimeInterval:0.5];
//模擬圖片下載速度慢
if (indexPath.row > 9) {
[NSThread sleepForTimeInterval:10];
}
//下載圖片
NSLog(@"下載網路圖片...");
NSURL *url = [NSURL URLWithString:appInfo.icon];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *img = [UIImage imageWithData:data];
//快取圖片
self.imageCache[appInfo.icon] = img;
//主執行緒上更新UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// cell.imageView.image = img;
//解決圖片顯示錯行
[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}];
}];
//把操作添加到佇列中
[self.queue addOperation:op];
//把操作添加到下載操作快取池中
self.downloadCache[appInfo.icon] = op;
//3 回傳cell
return cell;
}
//接收到記憶體警告
- (void)didReceiveMemoryWarning {
//清理記憶體
[self.imageCache removeAllObjects];
//
[self.downloadCache removeAllObjects];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//點擊cell的時候,輸出當前佇列的運算元
NSLog(@"佇列的運算元:%zd",self.queue.operationCount);
}
@end
@interface ViewController ()
@property (nonatomic, strong) NSArray *appInfos;
//全域佇列
@property (nonatomic, strong) NSOperationQueue *queue;
//圖片的快取池
@property (nonatomic, strong) NSMutableDictionary *imageCache;
//下載操作快取池
@property (nonatomic, strong) NSMutableDictionary *downloadCache;
@end
//1 創建模型類,獲取資料,測驗
//2 資料源方法
//3 同步下載圖片--如果網速比較慢,界面會卡頓
//4 異步下載圖片--圖片顯示不出來,點擊cell或者上下拖動圖片可以顯示
//解決,使用占位圖片
//5 圖片快取--把網路上下載的圖片,保存到記憶體--圖片存盤在模型物件中
//解決,圖片重復下載,把圖片快取到記憶體中,節省用戶的流量 (拿空間換取執行時間)
//6 解決圖片下載速度特別慢,出現的錯行問題,
//當圖片下載完成之后,重新加載對應的cell
//7 當收到記憶體警告,要清理記憶體,如果圖片存盤在模型物件中,不好清理記憶體
//圖片的快取池
//8 當有些圖片下載速度比較慢,上下不停滾動的時候,會重復下載圖片,會浪費流量
//判斷當前是否有對應圖片的下載操作,如果沒有添加下載操作,如果有不要重復創建操作
//下載操作的快取池
@implementation ViewController
//懶加載
- (NSArray *)appInfos {
if (_appInfos == nil) {
_appInfos = [HMAppInfo appInfos];
}
return _appInfos;
}
- (NSOperationQueue *)queue {
if (_queue == nil) {
_queue = [NSOperationQueue new];
}
return _queue;
}
- (NSMutableDictionary *)imageCache {
if (_imageCache == nil) {
_imageCache = [NSMutableDictionary dictionaryWithCapacity:10];
}
return _imageCache;
}
- (NSMutableDictionary *)downloadCache {
if (_downloadCache == nil) {
_downloadCache = [NSMutableDictionary dictionaryWithCapacity:10];
}
return _downloadCache;
}
- (void)viewDidLoad {
[super viewDidLoad];
//1 測驗模型資料
// NSLog(@"%@",self.appInfos);
}
//2 資料源方法
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.appInfos.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//1 創建可重用的cell
static NSString *reuseId = @"appInfo";
HMAppInfoCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseId];
if (cell == nil) {
cell = [[HMAppInfoCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseId];
}
//2 獲取資料,給cell內部子控制元件賦值
HMAppInfo *appInfo = self.appInfos[indexPath.row];
//cell內部的子控制元件都是懶加載的
//當回傳cell之前,會呼叫cell的layoutSubviews方法
cell.textLabel.text = appInfo.name;
cell.detailTextLabel.text = appInfo.download;
//判斷有沒有圖片快取
if (self.imageCache[appInfo.icon]) {
NSLog(@"從快取加載圖片...");
cell.imageView.image = self.imageCache[appInfo.icon];
return cell;
}
//設定占位圖片
cell.imageView.image = [UIImage imageNamed:@"user_default"];
//判斷下載操作快取池 中是否有對應的操作
if (self.downloadCache[appInfo.icon]) {
NSLog(@"正在拼命下載圖片...");
return cell;
}
//異步下載圖片
//模擬網速比較慢
//下載操作
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
// [NSThread sleepForTimeInterval:0.5];
//模擬圖片下載速度慢
if (indexPath.row > 9) {
[NSThread sleepForTimeInterval:10];
}
//下載圖片
NSLog(@"下載網路圖片...");
NSURL *url = [NSURL URLWithString:appInfo.icon];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *img = [UIImage imageWithData:data];
//快取圖片
self.imageCache[appInfo.icon] = img;
//主執行緒上更新UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// cell.imageView.image = img;
//解決圖片顯示錯行
[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}];
}];
//把操作添加到佇列中
[self.queue addOperation:op];
//把操作添加到下載操作快取池中
self.downloadCache[appInfo.icon] = op;
//3 回傳cell
return cell;
}
//接收到記憶體警告
- (void)didReceiveMemoryWarning {
//清理記憶體
[self.imageCache removeAllObjects];
[self.downloadCache removeAllObjects];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//點擊cell的時候,輸出當前佇列的運算元
NSLog(@"佇列的運算元:%zd",self.queue.operationCount);
}
@end
注意
- Block的回圈參考
- 代碼重構
- 把下載圖片的代碼提取成方法
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/207181.html
標籤:其他
上一篇:二十多歲的年紀是怎么成功四面位元組跳動,最終拿到offer的?
下一篇:Android專案:搭建一個基于MVVM的RxAndroid(RxJava)、Retrofit、OkHttp的專案
