iOS系列開(kāi)發(fā)-UITableView性能優(yōu)化
在我們的日常開(kāi)發(fā)中,很多開(kāi)發(fā)人員最常接觸的就是UITableView或者UICollectionView來(lái)布局某些列表等界面.
這里我們就拿UITableView來(lái)作為說(shuō)明內(nèi)容
絕大部分的時(shí)候,一個(gè)UITableView的內(nèi)容不會(huì)很多,cell的樣式\高度也不會(huì)很多元化,其僅僅作為一個(gè)展示用的UITableView來(lái)說(shuō),很多時(shí)候其性能都是很不錯(cuò)的.
但是也會(huì)有小眾的時(shí)候,一個(gè)作為列表展示的界面會(huì)有很多很多數(shù)據(jù),而且是實(shí)時(shí)的會(huì)加載很多新的內(nèi)容,表格的樣式也不唯一,有的僅有文字,有的僅有圖片,有的高度很長(zhǎng),在復(fù)雜點(diǎn)的很多cell雖然總體上差不多,但是卻會(huì)有很多或多或少的布局上的不一樣或者組件上的差距
當(dāng)然,并不是說(shuō)有了這些復(fù)雜的內(nèi)容,我們的UITableView就會(huì)性能變差,但是我們卻可以說(shuō)性能上比較差的UITableView,很多原因都是因?yàn)檫@些不定的因素,復(fù)雜的邏輯判斷,復(fù)雜的數(shù)據(jù)處理,復(fù)雜的圖形渲染,復(fù)雜的高度計(jì)算等等導(dǎo)致的.
而且如果表格的性能真的已經(jīng)變差了,那么其調(diào)優(yōu)的步驟是一定需要進(jìn)行的,否則作為開(kāi)發(fā)人員,我們過(guò)不了自己的關(guān),過(guò)不了測(cè)試的關(guān),過(guò)不了產(chǎn)品的關(guān)...
但是說(shuō)白了UITableView也就那么些方法
我們能夠利用的有哪些?
@property (nonatomic) CGFloat rowHeight;
是的,你會(huì)發(fā)現(xiàn)當(dāng)rowHeight時(shí)唯一固定的時(shí)候,往往性能不會(huì)太差
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
我們都知道,當(dāng)我們使用了代理的方法之后,上面的行高屬性就會(huì)失效了,但是相對(duì)應(yīng)的,我們需要給每一行都返回一個(gè)行高,這個(gè)是無(wú)法避免的,那么我們能否在這里做些優(yōu)化呢?
答案是肯定的,而且是顯著的.
為什么這么說(shuō)呢?
當(dāng)我們的UITableView是一個(gè)動(dòng)態(tài)行高的表格的話,我們以往的算法就是動(dòng)態(tài)計(jì)算,什么意思呢?
大致是這樣的
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
DetaileModel *model = _listArray[indexPath.row];//取出模型
return [DetailCell heigthForModel:model];//計(jì)算所需高度并返回
}
但是響應(yīng)的,因?yàn)閮?nèi)容的多樣化,可能有,這部分的計(jì)算本事雖然不會(huì)很耗時(shí),但是因?yàn)槲覀價(jià)eloadData的時(shí)候或者滾動(dòng)的時(shí)候,其都需要不停的計(jì)算高度并返回,這樣就會(huì)造成很多的展示表格或者創(chuàng)建或者重用表格cell的時(shí)候都需要耗時(shí)在計(jì)算高度的這部分上,那么我們?nèi)绾握{(diào)節(jié)呢?這部分計(jì)算肯定是需要的,此時(shí)你可能會(huì)說(shuō),iOS現(xiàn)在支持自適應(yīng)高度,
@property (nonatomic) CGFloat estimatedRowHeight NS_AVAILABLE_IOS(7_0); // default is 0, which means there is no estimate
是的,UITableView中,我們可以寫(xiě)一個(gè)預(yù)估行高,然后使用xib或者storyboard或者手動(dòng)layout的時(shí)候編寫(xiě)好約束的時(shí)候,即可動(dòng)態(tài)自動(dòng)的返回行高,但這樣的性能其實(shí)更差,時(shí)間全部都在調(diào)節(jié)約束上面了,然后硬生生的依靠約束來(lái)調(diào)節(jié)行高,這樣的性能只會(huì)更差,不推薦.
所以我們可以采取的是
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
DetaileModel *model = _listArray[indexPath.row];//取出模型
return model.cellHeight;//獲得所需高度并返回
}
如果我們把cellHeight當(dāng)做一個(gè)屬性,提前緩存好,之后直接獲取怎么樣呢?
此時(shí)你就會(huì)說(shuō),這樣有什么用嗎?
簡(jiǎn)單的說(shuō)幾點(diǎn)
1.使用內(nèi)存換性能
2.不需要重復(fù)計(jì)算
為什么這么說(shuō)呢?
首先我們會(huì)發(fā)現(xiàn),往往我們計(jì)算行高和設(shè)置cell的時(shí)候都是根據(jù)模型數(shù)據(jù)來(lái)判斷哪些需要顯示哪些不需要顯示,顯示的話顯示的位置和大小分別是多少,我們都是根據(jù)數(shù)據(jù)模型的內(nèi)容來(lái)判斷的.那么我們?nèi)绻@設(shè)置這個(gè)模型數(shù)據(jù)的時(shí)候(網(wǎng)絡(luò)請(qǐng)求回來(lái)json轉(zhuǎn)模型的時(shí)候),我們就根據(jù)這個(gè)模型的數(shù)據(jù)添加一個(gè)cellHeight的屬性,并且直接計(jì)算好保存在模型中,當(dāng)做模型的一個(gè)字段,那么我們?cè)趓eloadData的時(shí)候我們就會(huì)發(fā)現(xiàn)我們把計(jì)算高度的事情提前一步做好了,我們只需要返回model的cellHeight即可.而且因?yàn)槠浯鎯?chǔ)在該模型數(shù)據(jù)中,那么其跟模型一樣,一直是存在的,我們即可以在重新刷新表格或者滾動(dòng)視圖的時(shí)候都不需要再一次計(jì)算了(只要改模型存在,那么該cellHeight屬性就一直存在).
我們會(huì)發(fā)現(xiàn)僅僅是一個(gè)提前計(jì)算并存儲(chǔ)起來(lái),一個(gè)表格的性能就會(huì)大大提高,一些稍微不是特別復(fù)雜的表格此時(shí)已經(jīng)就能夠很流暢了.
至此我們會(huì)發(fā)現(xiàn)我們使用了類似設(shè)置cell的方式
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *cellid = @"cellid";
DetailCell *cell = [tableView dequeueReusableCellWithIdentifier:cellid];
if (!cell) {
cell = [[DetailCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellid];
}
cell.model = _listArray[indexPath.row];
return cell;
}
很多的開(kāi)發(fā)者都使用類似的方式,在cell中有一個(gè)model屬性,或者設(shè)置model的方法,以此來(lái)通過(guò)傳遞一個(gè)model來(lái)設(shè)置數(shù)據(jù),可能在自定義的設(shè)置方法中,可能在model的setter方法里面,此時(shí)我們又會(huì)一到一個(gè)新的問(wèn)題,某些model本身并不深很復(fù)雜,但是我們需要對(duì)這些數(shù)據(jù)做一些處理之后在做展示,比如圖文混排,比如拼接分割轉(zhuǎn)換等處理字符的顯示,這些在以往的時(shí)候,我們都是放在設(shè)置cell的數(shù)據(jù)源方法里面,通過(guò)模型的傳遞過(guò)來(lái),然后通過(guò)進(jìn)一步的轉(zhuǎn)化或者修改來(lái)決定如何顯示數(shù)據(jù). 但是同樣的,此時(shí),我們就需要不停的在表格更新顯示的時(shí)候來(lái)計(jì)算.
此時(shí)你肯定會(huì)想到,處理方法和上面一樣,提前計(jì)算好! 是的,我們?cè)谠O(shè)置模型的時(shí)候就提前計(jì)算好,在模型中添加新的字段,存儲(chǔ)這些需要計(jì)算的數(shù)據(jù),在cell展示的時(shí)候直接拿過(guò)來(lái)用即可.
但是問(wèn)題來(lái)了,新的數(shù)據(jù)模型仿佛不是數(shù)據(jù)模型了,參與了很多的計(jì)算(內(nèi)容計(jì)算,行高計(jì)算...)
那么我們能不能優(yōu)化一下呢?
這是我們之前的邏輯
這是我們現(xiàn)在的邏輯
我們能夠輕松的優(yōu)化了紅色部分的性能了
原因是我們把所有的需要提前計(jì)算的東西都提前了,將原來(lái)的需要計(jì)算的部分全部轉(zhuǎn)化成了一個(gè)內(nèi)存,使用內(nèi)存來(lái)代替之前的計(jì)算,在需要的時(shí)候只需要從內(nèi)存中直接拿出來(lái)使用即可.優(yōu)點(diǎn)上面簡(jiǎn)單的說(shuō)明了,只需要在賦值的時(shí)候計(jì)算好即可,之后就不需要計(jì)算了,以后的刷新都是直接從內(nèi)存中取出所需要的內(nèi)容或者行高即可
但是,相對(duì)應(yīng)的,模型就變的不在像模型了,(此時(shí)的模型中有了計(jì)算行高的部分,有了計(jì)算新的內(nèi)容的部分),并且跟網(wǎng)絡(luò)返回回來(lái)的模型已經(jīng)不是同樣的一個(gè)模型數(shù)據(jù)了.比原來(lái)有了更多的字段,或許在以后的時(shí)候中會(huì)有或多或少的問(wèn)題,比如字段重復(fù)卻不知道,比如模型傳遞,傳遞多次之后,已經(jīng)不知道哪部分是后來(lái)自己添加的屬性了...
那么我們使用新的數(shù)組來(lái)替代呢?我們使用一個(gè)新的數(shù)組來(lái)替代原來(lái)的模型數(shù)組
使用一個(gè)自建的模型類來(lái)代替原來(lái)的模型類,里面只有需要的內(nèi)容,比如行高,比如所有需要顯示的內(nèi)容,這樣的一個(gè)模型類來(lái)組成新的模型數(shù)組.
然后UITableView只需要處理新的模型數(shù)組來(lái)呈現(xiàn)即可.
但是這樣的的數(shù)組你會(huì)發(fā)現(xiàn),丟失了原來(lái)的模型.比如我們點(diǎn)擊某一個(gè)cell的時(shí)候我們需要將模型傳遞到下一個(gè)界面,此時(shí)拿到的數(shù)組就不是該模型了,而是我們自己定義的一個(gè)新的模型,這樣會(huì)不會(huì)就不太方便呢?
那么我們進(jìn)一步擴(kuò)展,我們創(chuàng)建一個(gè)DetailModelManager的類,這個(gè)類里面有這樣的一個(gè)屬性,model, 我們?cè)谄鋝et方法中做幾件事,第一件,就是普通的set,第二件是計(jì)算好一個(gè)cellHeight并保存為DetailModelManager的屬性,而不是model的一個(gè)屬性,計(jì)算好(或者賦值)所有需要顯示的內(nèi)容的字段,并保存為一個(gè)個(gè)的屬性
現(xiàn)在的邏輯變了
其實(shí)熟悉MVVM框架的同學(xué)此時(shí)就肯定笑話我了,這個(gè)不就是MVVM的VM嘛
是的,這其實(shí)就是MVVM的VM部分的一塊優(yōu)勢(shì)了,現(xiàn)在的列表數(shù)組已經(jīng)變成了DetailModelManager(ViewModel)數(shù)組了,所有的需要復(fù)雜計(jì)算的部分我們都可以以類似的方式來(lái)處理,比如我項(xiàng)目的一個(gè)列表
此時(shí)的列表不是單純的模型數(shù)組
tableView中寫(xiě)法稍變
原來(lái)所有的model部分轉(zhuǎn)化成viewModel部分
那么viewModel部分做了什么?
是的在之前的setModel的方法中我就做了很多事情,把后續(xù)需要計(jì)算的很多東西都計(jì)算結(jié)束并保存為屬性了
至于cell就簡(jiǎn)單了,原來(lái)的model換成viewModel,原來(lái)的model的屬性,現(xiàn)在變成了viewModel的屬性了,而且完全不需要任何計(jì)算.直接都是拿過(guò)來(lái)使用即可,
后期如果顯示的內(nèi)容不符合需求,只需要修改viewModel的那個(gè)字段的計(jì)算即可.其他任何地方都不需要修改.
呃,不知不覺(jué)說(shuō)了部分的MVVM的內(nèi)容,雖然和UITableView的性能優(yōu)化沒(méi)有關(guān)系,但是畢竟是性能優(yōu)化的一個(gè)實(shí)現(xiàn)方式,所以就一并說(shuō)了一點(diǎn)
- 總之就是行高一定要緩存
另外,我們肯能會(huì)遇到這樣的需求,一個(gè)稍微復(fù)雜的cell中可能有九宮格展示圖片(如新浪微博),可能有滾動(dòng)視圖展示圖片(如很多視頻類app),這個(gè)時(shí)候,我們會(huì)發(fā)現(xiàn),用來(lái)展示圖片的cell展示的內(nèi)容是動(dòng)態(tài)的,即數(shù)量是不定的,于是我們很多人就都會(huì)想到,九宮格啥的或者滾動(dòng)視圖啥的,我們都可以用coolectionView來(lái)實(shí)現(xiàn)嘛,完全動(dòng)態(tài)保證,多好...
但是其實(shí)這樣做仍然是有很多麻煩的.
首先是層級(jí)關(guān)系,一個(gè)cell中包含collectionView等雖然不會(huì)有很多影響,但是這個(gè)cell的代碼就會(huì)變的很龐大,很麻煩.而且動(dòng)態(tài)添加的方式會(huì)很消耗性能,原因上面也差不多說(shuō)了,我們上面一直就是在做表格的性能優(yōu)化,你在cell中又加一個(gè)表格,雖然不復(fù)雜,但是該遇到的性能問(wèn)題一個(gè)都不會(huì)丟的全部觸發(fā),而且或許是double.
如何做一點(diǎn)優(yōu)化呢?首先我們會(huì)發(fā)現(xiàn)諸如新浪微博的九宮格圖片展示樣式其實(shí)也是有規(guī)律的,其最多展示9張圖片.所以我們其實(shí)沒(méi)有必要使用動(dòng)態(tài)創(chuàng)建的方式,我們?cè)趧?chuàng)建cell的時(shí)候就創(chuàng)建9個(gè)imageView即可.在使用或者復(fù)用的時(shí)候,根據(jù)圖片個(gè)數(shù)或者展示需求來(lái)保證其顯示或者隱藏即可.這樣的cell你會(huì)發(fā)現(xiàn)在層次上和你創(chuàng)建collectionview是一樣的,但是在展示上,其非動(dòng)態(tài)獲取數(shù)據(jù)源展示和創(chuàng)建cell,其就是簡(jiǎn)單的設(shè)置圖片的顯示和隱藏即可,然后根據(jù)圖片的格式,調(diào)整好約束就好了.一個(gè)簡(jiǎn)單的處理我們會(huì)發(fā)現(xiàn)效果是很明顯的,這樣能盡可能的減少cell創(chuàng)建或從緩存池取時(shí)因?yàn)椴季肿涌丶牡臅r(shí)間.
所以說(shuō)
所有的子視圖都要預(yù)先創(chuàng)建
如果不需要顯示可以設(shè)置hidden
另外我們是否會(huì)發(fā)現(xiàn)我們?cè)陂_(kāi)發(fā)app的時(shí)候,A做push動(dòng)作到B.如果B沒(méi)有設(shè)置背景色的話,會(huì)出現(xiàn)一個(gè)明顯的卡頓?所以避免顏色導(dǎo)致問(wèn)題,我們最好都給子視圖設(shè)置好背景色.雖然不會(huì)有太大的影響,但是還是有點(diǎn)效果的
此外,柵格化也是我們可以關(guān)注的一點(diǎn),首先柵格化是設(shè)計(jì)中的術(shù)語(yǔ),但是用在我們的開(kāi)發(fā)同樣適用,當(dāng)我們遇到表格中的cell層級(jí)很多的時(shí)候,我們是可以做個(gè)柵格化的操作,就是將 cell 中的所有內(nèi)容,生成一張獨(dú)立的圖像,在屏幕滾動(dòng)時(shí),只顯示圖像 設(shè)置屬性 self.layer.shouldRasterize = YES;雖然代碼很簡(jiǎn)單,但是你會(huì)發(fā)現(xiàn)滾動(dòng)的效果會(huì)很明顯,當(dāng)然柵格化的同時(shí)必須指定分辨率,否則默認(rèn)使用 1倍的scale 生成圖像! 需要設(shè)置 self.layer.rasterizationScale = [UIScreen mainScreen].scale;
當(dāng)然既然有柵格化優(yōu)化滾動(dòng),那么還有操作來(lái)優(yōu)化其他的,比如繪制.
我們?cè)陂_(kāi)發(fā)中都會(huì)用到SDWebImage等類似的圖片異步加載的.來(lái)保證每個(gè)cell中的網(wǎng)絡(luò)圖片在加載數(shù)據(jù)的時(shí)候不會(huì)占用主線程,從而來(lái)保證cell的流暢性,當(dāng)然,其實(shí)我們不僅僅能做到這點(diǎn),我們還可以設(shè)置cell的異步繪制,如果 cell 比較復(fù)雜,可以設(shè)置cell圖層的屬性 self.layer.drawsAsynchronously = YES;代碼同樣簡(jiǎn)單的可以忽略,但是效果也是顯著的.
所以
- 異步加載是很需要的
所以總的來(lái)說(shuō)我們?cè)陂_(kāi)發(fā)表格的時(shí)候,我們需要關(guān)注的其實(shí)無(wú)非也就是簡(jiǎn)單的幾點(diǎn)
- 對(duì)行高的緩存------不要頻繁的動(dòng)態(tài)計(jì)算行高,少使用系統(tǒng)自動(dòng)預(yù)估和動(dòng)態(tài)行高,主動(dòng)負(fù)擔(dān)cell的布局,而不是依靠系統(tǒng)自動(dòng)'撐開(kāi)',在這塊處理上我們可以使用內(nèi)存換計(jì)算的方式,把內(nèi)存在模型構(gòu)建完成的時(shí)候就算出來(lái),提前保存好.之后直接使用即可.比如把行高計(jì)算放入model、viewmodel的初始化中或者設(shè)置數(shù)據(jù)的方法中,優(yōu)勢(shì)是不需要重復(fù)計(jì)算,減少計(jì)算行高時(shí)間花銷
- 子視圖提前創(chuàng)建,不要?jiǎng)討B(tài)創(chuàng)建------對(duì)于視圖的創(chuàng)建,每個(gè)人都有自己的方式,但是對(duì)于cell中來(lái)說(shuō),因?yàn)閏ell本身就是一個(gè)動(dòng)態(tài)的,所以在對(duì)其復(fù)用或者創(chuàng)建的時(shí)候我們就要盡量避免使用動(dòng)態(tài)的方式創(chuàng)建子視圖,無(wú)論是先removeSubViews再addSubView 亦或者是使用collectionView等動(dòng)態(tài)視圖,這些都要在復(fù)用的時(shí)候避免,如果可以,提前創(chuàng)建好需要的子視圖.在需要顯示的時(shí)候顯示,在不需要顯示的時(shí)候隱藏,這樣其創(chuàng)建的耗時(shí)工作僅在第一次創(chuàng)建的時(shí)候,之后的復(fù)用都不會(huì)再次發(fā)生創(chuàng)建和刪除視圖等操作,一個(gè)簡(jiǎn)單的隱藏就可以達(dá)到我們的需求,何樂(lè)不為?
- 圖片等展示異步加載------對(duì)于這一點(diǎn),大家都很有心得.或者說(shuō)大家都會(huì)在開(kāi)發(fā)的時(shí)候很直接的做到.如果講cell中的網(wǎng)絡(luò)圖片的加載使用主線程,那么何止是cell會(huì)卡頓,簡(jiǎn)直都是太小白的操作了.
- 盡量減少cell的層級(jí)------子視圖的層級(jí)不易過(guò)多,(不僅僅是cell的層級(jí),整個(gè)應(yīng)用的視圖中過(guò)多)層級(jí)如果太多,系統(tǒng)對(duì)視圖的渲染壓力就會(huì)相應(yīng)的變大,畢竟很明顯的道理是越少越好蠻.至于如何減少cell的層級(jí),很多方式,比如我們使用drewRect的方式來(lái)繪制文字或者圖片,而不使用label等,比如我們使用UItextField來(lái)充當(dāng)label,這樣既可以在某些時(shí)候僅展示文字,某些時(shí)候用來(lái)編輯文字,做到一個(gè)控件兩種使用方式(在類似需要填寫(xiě)或者選擇等信息的界面中我都會(huì)這么用),當(dāng)然如果你對(duì)性能要求很高,且技術(shù)足夠,你可以完全手工繪制cell,對(duì)于此你可以簡(jiǎn)單的學(xué)習(xí)setNeedsDisplay
- 圖層顏色的選擇------透明圖層對(duì)渲染性能會(huì)有一定的影響,系統(tǒng)必須將透明圖層與下面的視圖混合起來(lái)計(jì)算顏色,并繪制出來(lái)。減少透明圖層并使用不透明的圖層來(lái)替代它們,可以極大地提高渲染速度。
- 為代理方法瘦身-----自從我們學(xué)會(huì)的block的時(shí)候我們會(huì)發(fā)現(xiàn)我們?cè)谌魏蔚胤蕉剂?xí)慣使用block的方式來(lái)代替代理,在cell的創(chuàng)建中也不例外.比如cell中包含按鈕的點(diǎn)擊事件,我們現(xiàn)在都會(huì)使用block的方式來(lái)傳遞出來(lái),但是不知不覺(jué)中cell的代理方法我們會(huì)發(fā)現(xiàn)很龐大,其實(shí)這樣做是否真的好呢?其實(shí)不是,首先龐大的代碼就會(huì)影響閱讀,其次,每一個(gè)block都會(huì)開(kāi)辟一個(gè)空間,所以說(shuō)你別看只寫(xiě)了一個(gè)block,但是相對(duì)應(yīng)的你卻付出了block*個(gè)數(shù)的內(nèi)存,但其實(shí)他們的內(nèi)容完全一致,代理之所以存在是有其意義的,一味的使用block來(lái)替換代理其實(shí)是不對(duì)的,我們要在合適的時(shí)候用合適的方法.cell中的按鈕點(diǎn)擊,諸如此類的,仍然建議還是使用代理傳遞出來(lái).你不會(huì)發(fā)現(xiàn)性能上的優(yōu)化,你卻能發(fā)現(xiàn)代碼上的優(yōu)化.
- 減少離屏渲染------離屏渲染,指的是GPU在當(dāng)前屏幕緩沖區(qū)以外新開(kāi)辟一個(gè)緩沖區(qū)進(jìn)行渲染操作。設(shè)置了以下屬性時(shí),都會(huì)觸發(fā)離屏繪制:shouldRasterize(光柵化)masks(遮罩)shadows(陰影)edge antialiasing(抗鋸齒)group opacity(不透明)復(fù)雜形狀設(shè)置圓角等 漸變... ,我們的cell開(kāi)發(fā)雖然不在,但是不會(huì)太過(guò)分,我們用到的比較多的就是幾種,如圓角圖片,其會(huì)造成很明顯的離屏渲染,從而造成性能上的變差.所以肯定是要對(duì)其優(yōu)化的,當(dāng)然網(wǎng)絡(luò)上有很多教你設(shè)置圓角圖片的貼文,不仿看看
- cell柵格化------當(dāng)shouldRasterize設(shè)成true時(shí),layer被渲染成一個(gè)bitmap,并緩存起來(lái),等下次使用時(shí)不會(huì)再重新去渲染了。實(shí)現(xiàn)圓角本身就是在做顏色混合(blending),如果每次頁(yè)面出來(lái)時(shí)都blending,消耗太大,這時(shí)shouldRasterize = yes,下次就只是簡(jiǎn)單的從渲染引擎的cache里讀取那張bitmap,節(jié)約系統(tǒng)資源。如果在滾動(dòng)tableView時(shí),每次都執(zhí)行圓角設(shè)置,肯定會(huì)阻塞UI,設(shè)置這個(gè)將會(huì)使滑動(dòng)更加流暢。
- 異步加載------不僅僅是網(wǎng)絡(luò)圖片的異步加載.drawsAsynchronously屬性也是我們需要關(guān)注的.
- 性能優(yōu)化不是唯一的固定的,如果你的性能足夠,那么你不優(yōu)化也行,如果你的性能不夠,如何優(yōu)化,優(yōu)化哪些都是可選的.非固定的
性能上的優(yōu)化不僅僅如此,不僅僅是表格,
在這里推薦一部iOS開(kāi)發(fā)必看書(shū)籍,如果融會(huì)貫通,你的開(kāi)發(fā)經(jīng)驗(yàn)足以讓別人仰視對(duì)待,
ios核心動(dòng)畫(huà)高級(jí)技巧(https://www.gitbook.com/book/zsisme/ios-/details)