影響性能的原因有很多種,以下列舉最主要的幾點: 1、 cellForRowAtIndexPath方法中處理了過多的業務。 2、 tableViewCell的SubView層級太復雜,做了大量的透明處理。 3、 cell的height動態變化時計算方法不對。
關于影響性能的前兩點比較好理解,此文檔主要針對第三種情況做以處理。想要對tableView做性能優化,只能從tableView的數據源方法入手,而tableView的代理方法中,主要有兩個方法為我們所用:cellForRow和heightForRow。下面我們針對這兩個方法進行深入分析,并給出解決方案。
tableView:heightForRowAtIndexPath
TableView在每次reload data 時都需要所有cell的高度。也就是說如果有100行cell,那么每調用一次reload data ,就會調用100次heightForRowAtIndexPath方法獲取每個cell的高度,而不是說只獲取當前屏幕顯示的cell的高度。對于高度的計算,這里有個小細節需要注意,如果所有cell的高度都固定,那么就刪除代理中的這個tableView:heightForRowAtIndexPath:方法。設置tableView的rowHeight屬性。蘋果的文檔里也介紹了這樣可以減少調用時間。那么對于類似微博、微信朋友圈使用動態的高度,不定數量的文字,可能會有圖片,數量和大小也不固定的情況下,傳統的方法是為cell寫個計算行高的類方法,傳入那些動態的元素(文字,圖片等),然后返回計算后的高度。在tableView:heightForRowAtIndexPath:中調用這個方法,填入需要的參數來計算cell高度。當然這沒有什么問題,而且我們程序里也是這么做的。但是如果計算量很復雜,每次reload Data,光計算行高就花去了rowCount * 單行高的計算時間。假如有100行或更多時,程序里可能不定期的需要reload data 或者insertRow亦或者deleteRow,那么調用時間和內存消耗都很大。解決辦法:用“空間換時間”
用“空間換時間”簡單說就是將計算行高的時間提前到從服務器返回數據的時候,計算完了行高一并寫入數據庫中。2. tableView:cellForRowAtIndexPath:
在cellforRow回調的優化上,思路同上,也是通過預處理減少在這個回調中的計算時間。不過,主要針對圖片的異步加載進行優化。 圖片的異步加載無非就是在這個方法里發起異步請求,圖片加載完后根據UIImageView的引用設置圖片。在這里,使用懶加載的方式減少快速滑動時因為網絡請求過于頻繁與切換線程顯示圖片造成卡頓。還有個細節,從服務器拿回來的圖片和最后顯示的大小可能不一樣。這就涉及到對圖片的壓縮。在顯示時,控件加了約束,圖片可能會適應控件大小,圖片變形需要對圖片做transform。每次壓縮圖片都要對圖片乘以一個變換矩陣,如果圖片很多,這個計算量是不容忽視的。優化建議:
從網絡拿回圖片后,先根據需要顯示的圖片大小切成適合大小的圖,每次只顯示處理過大小的圖片。當查看大圖時再顯示大圖,如果服務器能直接返回預處理好的小圖和圖片的大小更好。Ps:我們公司的程序里,文件服務器會有多種格式的圖片大小,每次請求時會附帶大小規格,但是,目前文件服務器支持的小圖規格和UI上的控件大小還是不一致,還是會進行壓縮。
// 等比縮放
- (UIImage *)hyb_cropEqualScaleImageToSize:(CGSize)size { CGFloat scale = [UIScreen mainScreen].scale; // 這一行至關重要 // 不要直接使用UIGraphicsBeginImageContext(size);方法 // 因為控件的frame與像素是有倍數關系的 // 比如@1x、@2x、@3x圖,因此我們必須要指定scale,否則黃色去不了 // 因為在5以上,scale為2,6plus scale為3,所生成的圖是要合蘋果的 // 規格才能正常 UIGraphicsBeginImageContextWithOptions(size, NO, scale); CGSize aspectFitSize = CGSizeZero; if (self.size.width != 0 && self.size.height != 0) { CGFloat rateWidth = size.width / self.size.width; CGFloat rateHeight = size.height / self.size.height; CGFloat rate = MIN(rateHeight, rateWidth); aspectFitSize = CGSizeMake(self.size.width * rate, self.size.height * rate); } [self drawInRect:CGRectMake(0, 0, aspectFitSize.width, aspectFitSize.height)]; UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return image;}直接縮放至指定大小// 非等比縮放,生成的圖片可能會被拉伸
- (UIImage *)hyb_cropEqualScaleImageToSize:(CGSize)size { CGFloat scale = [UIScreen mainScreen].scale; UIGraphicsBeginImageContextWithOptions(size, NO, scale); [self drawInRect:CGRectMake(0, 0, size.width, size.height)]; UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return image; }聲明:本文部分內容參考于編程小翁和標哥博客。
新聞熱點
疑難解答