1.OS app啟動如何優(yōu)化望伦?
1. 我們可以通過在 Xcode 中 Edit scheme -> Run -> Auguments 將環(huán)境變量 DYLD_PRINT_STATISTICS 設(shè)為 1,在控制臺看到main()函數(shù)之前的啟動時(shí)間。
2. 分解優(yōu)化目標(biāo) 分步達(dá)到優(yōu)化目的
1). 耗時(shí)操作異步處理
2). 如果啟動流程依賴網(wǎng)絡(luò)請求回來才能繼續(xù),那么需要考慮網(wǎng)絡(luò)極差情況下的啟動速度
3). 如果APP有l(wèi)oading廣告頁并且對分辨率的要求較高,請嘗試做緩存吧
4). 主頁面Controller中的viewDidLoad和viewWillAppear方法中盡量少做事情
5). 排查清理項(xiàng)目中未使用到的類庫以及Framework
6). 刪減合并一些OC類,刪減沒有用到或者可以不用的靜態(tài)變量肪笋、方法等
7). 輕量化+load方法中的內(nèi)容,可延遲到+initialize中
2.copy與mutableCopy的理解?
使用copy或mutableCopy方法可以創(chuàng)建一個對象的副本
copy
需要實(shí)現(xiàn)NSCoppying協(xié)議
這些創(chuàng)建的是不可變副本(如NSString潮罪、NSArray兼犯、NSDictionary)
Copy的目的是建立副本,同時(shí)修改原始對象和復(fù)本不會互相干擾
mutableCopy
需要先實(shí)現(xiàn)NSMutableCopying協(xié)議
創(chuàng)建的是可變副本(如NSMutableString、NSMutableArray合蔽、NSMutableDictionary)
3.UITableView性能優(yōu)化
1)Cell重用
- 1.1>數(shù)據(jù)源方法優(yōu)化
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
在可見的頁面會重復(fù)繪制頁面,每次刷新顯示都會去創(chuàng)建新的Cell满粗,非常耗費(fèi)性能辈末。
** 解決方案:**首先創(chuàng)建一個靜態(tài)變量reuseID(代理方法返回Cell會調(diào)用很多次,防止重復(fù)創(chuàng)建映皆,static保證只會被創(chuàng)建一次挤聘,提高性能),然后捅彻,從緩存池中取相應(yīng)identifier的Cell并更新數(shù)據(jù)组去,如果沒有,才開始alloc新的Cell步淹,并用identifier標(biāo)識Cell从隆。每個Cell都會注冊一個identifier(重用標(biāo)識符)放入緩存池,當(dāng)需要調(diào)用的時(shí)候就直接從緩存池里找對應(yīng)的id缭裆,當(dāng)不需要時(shí)就放入緩存池等待調(diào)用键闺。(移出屏幕的Cell才會放入緩存池中,并不會被release)所以在數(shù)據(jù)源方法中做出如下優(yōu)化:
// 調(diào)用次數(shù)太多澈驼,static 保證只創(chuàng)建一次reuseID辛燥,提高性能
static NSString *reuseID = “reuseCellID”;
// 緩存池中取已經(jīng)創(chuàng)建的cell
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseID];
- 1.2>緩存池的實(shí)現(xiàn)
當(dāng)Cell要alloc時(shí),UITableView會在堆中開辟一段內(nèi)存以供Cell緩存之用缝其。Cell的重用通過identifier標(biāo)識不同類型的Cell挎塌,由此可以推斷出,緩存池外層可能是一個可變字典内边,通過key來取出內(nèi)部的Cell榴都,而緩存池為存儲不同高度、不同類型(包含圖片漠其、Label等)的Cell嘴高,可以推斷出緩存池的字典內(nèi)部可能是一個可變數(shù)組,用來存放不同類型的Cell辉懒,緩存池中只會保存已經(jīng)被移出屏幕的不同類型的Cell阳惹。 - 1.3>緩存池獲取可重用Cell兩個方法的區(qū)別
-(nullable __kindof UITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier;
這個方法會查詢可重用Cell,如果注冊了原型Cell眶俩,能夠查詢到,否則快鱼,返回nil颠印;而且需要判斷if(cell == nil)纲岭,才會創(chuàng)建Cell,不推薦
-(__kindof UITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(6_0);
使用這個方法之前线罕,必須通過xib(storyboard)或是Class(純代碼)注冊可重用Cell止潮,而且這個方法一定會返回一個Cell
注冊Cell
- (void)registerNib:(nullable UINib *)nib forCellReuseIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(5_0);
- (void)registerClass:(nullable Class)cellClass forCellReuseIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(6_0);
好處:如果緩沖區(qū) Cell 不存在,會使用原型 Cell 實(shí)例化一個新的 Cell钞楼,不需要再判斷喇闸,同時(shí)代碼結(jié)構(gòu)更清晰。
2)定義一種(盡量少)類型的Cell及善用hidden隱藏(顯示)subviews
-
2.1>一種類型的Cell
分析Cell結(jié)構(gòu)询件,盡可能的將 相同內(nèi)容的抽取到一種樣式Cell中燃乍,前面已經(jīng)提到了Cell的重用機(jī)制,這樣就能保證UITbaleView要顯示多少內(nèi)容宛琅,真正創(chuàng)建出的Cell可能只比屏幕顯示的Cell多一點(diǎn)刻蟹。雖然Cell的’體積’可能會大點(diǎn),但是因?yàn)镃ell的數(shù)量不會很多嘿辟,完全可以接受的舆瘪。好處:- 減少代碼量,減少Nib文件的數(shù)量红伦,統(tǒng)一一個Nib文件定義Cell英古,容易修改、維護(hù)
- 基于Cell的重用昙读,真正運(yùn)行時(shí)鋪滿屏幕所需的Cell數(shù)量大致是固定的召调,設(shè)為N個。所以如果如果只有一種Cell箕戳,那就是只有N個Cell的實(shí)例某残;但是如果有M種Cell,那么運(yùn)行時(shí)最多可能會是“M x N = MN”個Cell的實(shí)例陵吸,雖然可能并不會占用太多內(nèi)存玻墅,但是能少點(diǎn)不是更好嗎。
2.2>善用hidden隱藏(顯示)subviews
只定義一種Cell壮虫,那該如何顯示不同類型的內(nèi)容呢澳厢?答案就是,把所有不同類型的view都定義好囚似,放在cell里面剩拢,通過hidden顯示、隱藏饶唤,來顯示不同類型的內(nèi)容徐伐。畢竟,在用戶快速滑動中募狂,只是單純的顯示办素、隱藏subview比實(shí)時(shí)創(chuàng)建要快得多角雷。
3)提前計(jì)算并緩存Cell的高度
在iOS中,不設(shè)UITableViewCell的預(yù)估行高的情況下性穿,會優(yōu)先調(diào)用”tableView:heightForRowAtIndexPath:”方法勺三,獲取每個Cell的即將顯示的高度,從而確定UITableView的布局需曾,實(shí)際就是要獲取contentSize(UITableView繼承自UIScrollView,只有獲取滾動區(qū)域吗坚,才能實(shí)現(xiàn)滾動),然后才調(diào)用”tableView:cellForRowAtIndexPath”,獲取每個Cell,進(jìn)行賦值呆万。如果項(xiàng)目中模塊有10000個Cell需要顯示商源,可想而知…
解決方案:我個人認(rèn)為,可以創(chuàng)建一個frame模型桑嘶,提前計(jì)算每個Cell的高度炊汹。參考其中一篇博客的時(shí)候,在解決這個問題的時(shí)候逃顶,可以將計(jì)算Cell的高度放入數(shù)據(jù)模型讨便,但這與MVC設(shè)計(jì)模式可能稍微有點(diǎn)沖突,這個時(shí)候我就想到MVVM這種設(shè)計(jì)模式以政,這個時(shí)候才能稍微有點(diǎn)MVVM這種設(shè)計(jì)模式的優(yōu)點(diǎn)(其實(shí)還是很不理解的)霸褒,可以講計(jì)算Cell高度放入ViewModel(視圖模型)中,讓Model(數(shù)據(jù)模型)只負(fù)責(zé)處理數(shù)據(jù)盈蛮。
4)異步繪制(自定義Cell繪制)
遇到比較復(fù)雜的界面的時(shí)候废菱,如復(fù)雜點(diǎn)的圖文混排,上面的那種優(yōu)化行高的方式可能就不能滿足要求了抖誉,當(dāng)然了殊轴,由于我的開發(fā)經(jīng)驗(yàn)尚短,說實(shí)話袒炉,還沒遇到要將自定義的Cell重新繪制
5)滑動時(shí)旁理,按需加載
開發(fā)的過程中,自定義Cell的種類千奇百怪我磁,但Cell本來就是用來顯示數(shù)據(jù)的孽文,不說100%帶有圖片,也差不多夺艰,這個時(shí)候就要考慮芋哭,下滑的過程中可能會有點(diǎn)卡頓,尤其網(wǎng)絡(luò)不好的時(shí)候郁副,異步加載圖片是個程序員都會想到减牺,但是如果給每個循環(huán)對象都加上異步加載,開啟的線程太多,一樣會卡頓烹植,我記得好像線程條數(shù)一般3-5條斑鸦,最多也就6條吧愕贡。這個時(shí)候利用UIScrollViewDelegate兩個代理方法就能很好地解決這個問題草雕。
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
6)緩存View
當(dāng)Cell中的部分View是非常獨(dú)立的,并且不便于重用的固以,而且“體積”非常小墩虹,在內(nèi)存可控的前提下,我們完全可以將這些view緩存起來憨琳。當(dāng)然也是緩存在模型中诫钓。
7)避免大量的圖片縮放、顏色漸變等篙螟,盡量顯示“大小剛好合適的圖片資源”
8)避免同步的從網(wǎng)絡(luò)菌湃、文件獲取數(shù)據(jù),Cell內(nèi)實(shí)現(xiàn)的內(nèi)容來自web遍略,使用異步加載惧所,緩存請求結(jié)果
9)渲染
- 9.1>減少subviews的個數(shù)和層級
子控件的層級越深,渲染到屏幕上所需要的計(jì)算量就越大绪杏;如多用drawRect繪制元素下愈,替代用view顯示 - 9.2>少用subviews的透明圖層
對于不透明的View,設(shè)置opaque為YES蕾久,這樣在繪制該View時(shí)势似,就不需要考慮被View覆蓋的其他內(nèi)容(盡量設(shè)置Cell的view為opaque,避免GPU對Cell下面的內(nèi)容也進(jìn)行繪制) - 9.3>避免CALayer特效(shadowPath)
給Cell中View加陰影會引起性能問題僧著,如下面代碼會導(dǎo)致滾動時(shí)有明顯的卡頓:
view.layer.shadowColor = color.CGColor;
view.layer.shadowOffset = offset;
view.layer.shadowOpacity = 1;
view.layer.shadowRadius = radius;