作者:Love@YR
鏈接:http://blog.csdn.net/jingqiu880905/article/details/51920117
請尊重原創,謝謝!
UITableView的優化是個難點也是個痛點,下面列出各路大神總結的優化技巧。
首先,基礎篇:
cell繪製方面cellForRowAtIndexPath:
cell重用 reuseIdentifier
這個可以看下上一篇文章,講解了不同情況下alloc了多少個cell就比較清楚了。不用reuse你內存受不了。
(也應該在section header和footer中使用reuseIdentifier)統一設計cell
cell的Prototype最好高度抽象統一,越少越好,因爲不同的Prototype有其對應的cell重用池,創建的cell種類越多,重用效率也就越低。所以能用一種就用一種。減少視圖層級關係,減少subview的數量
儘量少用addView給cell動態添加view,可以初始化時就添加,然後通過hide來控制是否顯示。儘量使用opaque
儘量使cell的contentView所有的subview都設置opaque,包括Cell自身。儘量少用或不用透明圖層。繪製方法中儘量不要處理過多業務邏輯
高度計算方面heightForRowAtIndexPath:
- row 的高度都一定的情況
刪除代理中的heightForRowAtIndexPath: 方法,設置 tableView的rowHeight屬性,sectionHeaderHeight footer也同樣的道理。
其他:
cell數據資源緩存
一般大家也都會直接用個dictionary或者數組或者model來存下需要顯示的數據源,不大可能會到用的時候再去讀plist啊,拿數據庫裏的之類。沒啥好說的。UI的尺寸和行高緩存,用 “空間換時間”
正常情況計算行高放在heightForRowAtIndexPath,但如果這個計算很複雜,時間不固定,有時短有時長,你再一reloaddata,或者insertrow,deleterow那就很煩了。
所以將計算行高的時間(當然連同各UI的尺寸計算,你只有算出來所有子view高寬度才能確定cell高度)提前到從服務器拿到數據的時候就開始,計算完了將cell高度,各行各ui的frame一併寫回數據源預處理。
一次計算一勞永逸,避免在heightForRowAtIndexPath和cellForRowAtIndexPath方法裏每次滾到這一行都去計算。圖片預處理
顯示之前提前處理。比如縮放:
如果你是在畫cell的時候直接設置imageview的 contentMode 屬性讓 imageview自己縮放,縮放需要對圖片做transform ,要對圖片乘以一個變換矩陣,計算量很大,很耗性能,更何況你每次滾到那行都要縮放。
所以可以在剛拿到圖片就先縮放生成新圖,當然最好是服務端能返回相對應大小的圖片,這樣就不用縮放了。而如果要看大圖,最好是點擊查看大圖的時候再拿大圖去渲染。設計時不宜把大圖直接顯示在列表裏。圖片異步加載
圖片下載放到後臺,異步加載。
不過如果每個循環對象都異步加載,很多個線程也會影響主線程的性能,所以好的方法就是按需加載,而不是全部在後臺下載圖片按需加載
沒有在滑動且加速度爲0(即已經停止下來時)纔去加載圖片(不然你滑那麼快我每個圖都去請求網絡下載然後顯示,每個下載又是一個子線程,我線程各種切換,然後你刷地又滑回來。。。),且只去畫當前可視區域的圖片,或者再加上當前可視前後指定的幾行。
下載過的不再下載,正在下載的不去下載。
(按需加載可參考上篇文章,蘋果官方例子lazyloadImage的解析)圖片資源儘量用png,因爲iOS本身對png進行了很多優化
關於reloaddata
heightForRowAtIndexPath 在reload data時會執行所有cell高度全部刷新,而不是當前屏幕顯示的cell數量。儘量不用reload data而用insertrow deleterow關於重複代碼
heightForRowAtIndexPath和cellForRowAtIndexPath中儘量不要有重複代碼儘量少用xib storyboard創建cell,他們需要系統自動轉碼,也不能自定義cell繪製
其次,關於cell上圖片圓角
- cornerRadius方式
cell.myImageView.layer.cornerRadius = 8.0;
cell.myImageView.layer.masksToBounds = YES;//或者cell.myImageView.clipsToBounds = YES
這種方式會觸發離屏渲染。iOS9之後png圖片不會了。
- 設置masklayer
CAShapLayer *layer = [CAShapLayer layer];
UIBezierPath *bzpath = [UIBezierPath bezierPathWithOvalInRect:imageview.bounds] ;
layer.path = bzpath.CGPath;
cell.myImageView.layer.mask = layer
一次mask發生兩次離屏渲染和一次主屏渲染,比上面的方法效率還低。(主要是上下文切換耗性能)
但是可不侷限於圓角,由 mask 控制邊角顯示爲什麼形狀。
- 光柵化,也叫位圖化
cell.myImageView.layer.cornerRadius = 8.0;
cell.myImageView.layer.masksToBounds = YES;
cell.myImageView.layer.shouldRasterize = YES;
cell.myImageView.layer.rasterizationScale = [UIScreen mainScreen].scale;//UIImageView不加這句會產生一點模糊
設置光柵化,可以使離屏渲染的結果緩存到內存中存爲位圖,使用的時候直接使用緩存,節省了一直離屏渲染損耗的性能。但是如果layer及sublayers常常改變的話,它就會一直不停的渲染及刪除緩存重新創建緩存,此情況下建議不要使用光柵化,比較損耗性能
- 對圖片進行切角
imageview的drawRect或者setImage裏
UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, [UIScreen mainScreen].scale);
[[UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:self.bounds.size.height/2] addClip];
[imageFromServer drawInRect:self.bounds];
circleImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[super setImage:circleImage]
切角的操作是在CPU內完成的,而後我們只需要取到處理完成的bitmap(可爲UIImage對象)交給GPU顯示於屏幕即可。
無離屏渲染,把 GPU的壓力轉給 CPU,適用於 CPU 壓力不大的情況,CPU和內存消耗增大。
- mask圖和原圖合成
UIImage *roundedImage = [self imageByComposingImage:image
withMaskImage:[UIImage imageNamed:@"mask.png"]];
cell.myImageView.image = roundedImage;
- (UIImage *)imageByComposingImage:(UIImage *)image withMaskImage:(UIImage *)maskImage {
CGImageRef maskImageRef = maskImage.CGImage;
CGImageRef maskRef = CGImageMaskCreate(CGImageGetWidth(maskImageRef),
CGImageGetHeight(maskImageRef),
CGImageGetBitsPerComponent(maskImageRef),
CGImageGetBitsPerPixel(maskImageRef),
CGImageGetBytesPerRow(maskImageRef),
CGImageGetDataProvider(maskImageRef), NULL, false);
CGImageRef newImageRef = CGImageCreateWithMask(image.CGImage, maskRef);
CGImageRelease(maskRef);
UIImage *newImage = [UIImage imageWithCGImage:newImageRef];
CGImageRelease(newImageRef);
return newImage;
}
不侷限圓角,但效率比較低。
- 直接覆蓋一張中間爲圓形透明的圖片
直接[imageview addsubview:中間透明的圖片]
然後再[imageview setImage:原圖]
這種方法就是多加了一張透明的圖片,GPU計算多層的混合渲染blending也是會消耗一點性能的
最後,關於Instrument來調試tableView性能
Core Animation
debug options選擇:
Color Hits Green and Misses Red:
光柵化對應的渲染結果會被緩存。如果圖層是綠色,就表示這些緩存被複用;如果是紅色就表示緩存會被重複創建,這就表示該處存在性能問題了。Color Offscreen-Rendered Yellow開啓後會把那些需要離屏渲染的圖層高亮成黃色,這就意味着黃色圖層可能存在性能問題。
Color misaligned images:
洋紅色代表像素沒對齊???
黃色代表圖片有縮放
解決方法是設置各view 的 frame 時用整數不用小數??
和最好剛拿到圖時就縮放生成新圖而不是顯示時再縮放GPU Driver
Renderer Utilization
如果這個值超過了~50%,就意味着你的動畫可能對幀率有所限制,很可能因爲離屏渲染或者是重繪導致的過度混合Tiler Utilization
如果這個值超過了~50%,就意味着你的動畫可能限制於幾何結構方面,也就是在屏幕上有太多的圖層佔用了。幀率Core animation frames per seconds 越接近60滑動越順暢。
time profiler
Call Tree那邊設置全選,去查看哪句代碼走的時間比較長
參考:http://tutuge.me/2015/02/19/提升UITableView性能-複雜頁面的優化/
http://blog.csdn.net/hanmingsa/article/details/51648199