1、NSOperation VS GCD
- GCD
- GCD是iOS4.0 推出的,主要針對多核cpu作了優化,是C語言的技術
- GCD是將任務(block)添加到隊列(串行/並行/全局/主隊列),而且以同步/異步的方式執行任務的函數
- GCD提供了一些NSOperation不具有的功能
- 一次性執行
- 延遲執行
- 調度組
- NSOperation
- NSOperation是iOS2.0推出的,iOS4以後重寫了NSOperation
- NSOperation將操做(異步的任務)添加到隊列(併發隊列),就會執行指定操做的函數
- NSOperation裏提供的方便的操做
- 最大併發數
- 隊列的暫定/繼續
- 取消全部的操做
- 指定操做之間的依賴關係(GCD能夠用同步實現)
2、最大併發數
- 什麼是併發數
同時執行的任務數數組
好比,同時開3個線程執行3個任務,併發數就是3緩存
- 最大併發數的相關方法
- (NSInteger)maxConcurrentOperationCount; - (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
- 執行的過程
- 一、把操做添加到隊列self.queue addOperationWithBlock
- 二、去線程池去取空閒的線程,若是沒有就建立線程
- 三、把操做交給從線程池中取出的線程執行
- 四、執行完成後,把線程再放回線程池中
- 五、重複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); }
3、操做的優先級
- 設置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
4、操做依賴
- NSOperation之間能夠設置依賴來保證執行順序
好比必定要讓操做A執行完後,才能執行操做B,能夠這麼寫網絡
[operationB addDependency:operationA]; // 操做B依賴於操做A
能夠在不一樣queue的NSOperation之間建立依賴關係併發
模擬軟件升級過程:下載—解壓—升級完成app
@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];
提供方法—---生成全部對象的一個集合dom
+ (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的循環引用
- 代碼重構
- 把下載圖片的代碼提取成方法