W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
????繪圖實際消耗的時間通常并不是影響性能的因素。圖片消耗很大一部分內(nèi)存,而且不太可能把需要顯示的圖片都保留在內(nèi)存中,所以需要在應用運行的時候周期性地加載和卸載圖片。
????圖片文件加載的速度被CPU和IO(輸入/輸出)同時影響。iOS設備中的閃存已經(jīng)比傳統(tǒng)硬盤快很多了,但仍然比RAM慢將近200倍左右,這就需要很小心地管理加載,來避免延遲。
????只要有可能,試著在程序生命周期不易察覺的時候來加載圖片,例如啟動,或者在屏幕切換的過程中。按下按鈕和按鈕響應事件之間最大的延遲大概是200ms,這比動畫每一幀切換的16ms小得多。你可以在程序首次啟動的時候加載圖片,但是如果20秒內(nèi)無法啟動程序的話,iOS檢測計時器就會終止你的應用(而且如果啟動大于2,3秒的話用戶就會抱怨了)。
????有些時候,提前加載所有的東西并不明智。比如說包含上千張圖片的圖片傳送帶:用戶希望能夠能夠平滑快速翻動圖片,所以就不可能提前預加載所有圖片;那樣會消耗太多的時間和內(nèi)存。
????有時候圖片也需要從遠程網(wǎng)絡連接中下載,這將會比從磁盤加載要消耗更多的時間,甚至可能由于連接問題而加載失敗(在幾秒鐘嘗試之后)。你不能夠在主線程中加載網(wǎng)絡造成等待,所以需要后臺線程。
????在第12章“性能調(diào)優(yōu)”我們的聯(lián)系人列表例子中,圖片都非常小,所以可以在主線程同步加載。但是對于大圖來說,這樣做就不太合適了,因為加載會消耗很長時間,造成滑動的不流暢?;瑒觿赢嫊谥骶€程的run loop中更新,所以會有更多運行在渲染服務進程中CPU相關(guān)的性能問題。
????清單14.1顯示了一個通過UICollectionView
實現(xiàn)的基礎的圖片傳送器。圖片在主線程中-collectionView:cellForItemAtIndexPath:
方法中同步加載(見圖14.1)。
清單14.1 使用UICollectionView
實現(xiàn)的圖片傳送器
#import "ViewController.h"
@interface ViewController()
@property (nonatomic, copy) NSArray *imagePaths;
@property (nonatomic, weak) IBOutlet UICollectionView *collectionView;
@end
@implementation ViewController
- (void)viewDidLoad
{
//set up data
self.imagePaths =
[[NSBundle mainBundle] pathsForResourcesOfType:@"png" inDirectory:@"Vacation Photos"];
//register cell class
[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"Cell"];
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return [self.imagePaths count];
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
//dequeue cell
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];
//add image view
const NSInteger imageTag = 99;
UIImageView *imageView = (UIImageView *)[cell viewWithTag:imageTag];
if (!imageView) {
imageView = [[UIImageView alloc] initWithFrame: cell.contentView.bounds];
imageView.tag = imageTag;
[cell.contentView addSubview:imageView];
}
//set image
NSString *imagePath = self.imagePaths[indexPath.row];
imageView.image = [UIImage imageWithContentsOfFile:imagePath];
return cell;
}
@end
圖14.2 時間分析工具展示了CPU瓶頸
????這里提升性能唯一的方式就是在另一個線程中加載圖片。這并不能夠降低實際的加載時間(可能情況會更糟,因為系統(tǒng)可能要消耗CPU時間來處理加載的圖片數(shù)據(jù)),但是主線程能夠有時間做一些別的事情,比如響應用戶輸入,以及滑動動畫。
????為了在后臺線程加載圖片,我們可以使用GCD或者NSOperationQueue
創(chuàng)建自定義線程,或者使用CATiledLayer
。為了從遠程網(wǎng)絡加載圖片,我們可以使用異步的NSURLConnection
,但是對本地存儲的圖片,并不十分有效。
NSOperationQueue
????GCD(Grand Central Dispatch)和NSOperationQueue
很類似,都給我們提供了隊列閉包塊來在線程中按一定順序來執(zhí)行。NSOperationQueue
有一個Objecive-C接口(而不是使用GCD的全局C函數(shù)),同樣在操作優(yōu)先級和依賴關(guān)系上提供了很好的粒度控制,但是需要更多地設置代碼。
????清單14.2顯示了在低優(yōu)先級的后臺隊列而不是主線程使用GCD加載圖片的-collectionView:cellForItemAtIndexPath:
方法,然后當需要加載圖片到視圖的時候切換到主線程,因為在后臺線程訪問視圖會有安全隱患。
????由于視圖在UICollectionView
會被循環(huán)利用,我們加載圖片的時候不能確定是否被不同的索引重新復用。為了避免圖片加載到錯誤的視圖中,我們在加載前把單元格打上索引的標簽,然后在設置圖片的時候檢測標簽是否發(fā)生了改變。
清單14.2 使用GCD加載傳送圖片
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
//dequeue cell
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell"
forIndexPath:indexPath];
//add image view
const NSInteger imageTag = 99;
UIImageView *imageView = (UIImageView *)[cell viewWithTag:imageTag];
if (!imageView) {
imageView = [[UIImageView alloc] initWithFrame: cell.contentView.bounds];
imageView.tag = imageTag;
[cell.contentView addSubview:imageView];
}
//tag cell with index and clear current image
cell.tag = indexPath.row;
imageView.image = nil;
//switch to background thread
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
//load image
NSInteger index = indexPath.row;
NSString *imagePath = self.imagePaths[index];
UIImage *image = [UIImage imageWithContentsOfFile:imagePath];
//set image on main thread, but only if index still matches up
dispatch_async(dispatch_get_main_queue(), ^{
if (index == cell.tag) {
imageView.image = image; }
});
});
return cell;
}
????當運行更新后的版本,性能比之前不用線程的版本好多了,但仍然并不完美(圖14.3)。
????我們可以看到+imageWithContentsOfFile:
方法并不在CPU時間軌跡的最頂部,所以我們的確修復了延遲加載的問題。問題在于我們假設傳送器的性能瓶頸在于圖片文件的加載,但實際上并不是這樣。加載圖片數(shù)據(jù)到內(nèi)存中只是問題的第一部分。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: