1,cell如果是用xib創(chuàng)建的锅劝,一定不能忘記在xib中填寫reuseIdentifier,否則不會重用
2蟆湖,cell的高度如果是根據(jù)內(nèi)容計算的故爵,則可以將計算的高度緩存到indexPath.row對應(yīng)的字典中,數(shù)據(jù)源沒有變化的時候不需要重新計算隅津。
3诬垂,cell中的頭像設(shè)計喜歡加圓角,如果用系統(tǒng)的方式.layer.maskToBounds和.cornerRadius會有離屏渲染伦仍,如果加圓角過多结窘,甚至還會引起屏幕卡頓,所以用裁剪的方式把圖片裁剪成圓角顯示呢铆。
4晦鞋,cell中圖片都使用與imageView大小最匹配的大小,避免加載大圖片耗費系統(tǒng)資源渲染棺克。
5悠垛,cell中盡量不設(shè)置陰影效果,如果要設(shè)置娜谊,用圖片的方式實現(xiàn)确买,系統(tǒng)的shadow會涉及離屏渲染,降低效率
6纱皆,coredata增加表湾趾,刪除表,增加派草,刪除變量都不需要新建版本搀缠,可以用他的自動輕量級遷移來實現(xiàn)版本遷移,但是如果變量類型有變化的話需要新建一個版本近迁,并寫map文件進行不同類型之間的遷移艺普。
7,如果需要用到循環(huán)創(chuàng)建view的話,重用的時候不要移除原view重新創(chuàng)建歧譬,而是要通過設(shè)置tag值岸浑,通過tag值取view的操作來復(fù)用view,
8瑰步,所有無關(guān)于UI刷新的任務(wù)都盡可能放到子線程去執(zhí)行矢洲,當線程需要不斷執(zhí)行任務(wù)時可設(shè)置runloop執(zhí)行,避免線程的頻繁創(chuàng)建和銷毀缩焦。
9读虏,內(nèi)存是app優(yōu)化中很重要的一部分,需要注意內(nèi)存泄漏舌界,以及內(nèi)存緩存的清空掘譬,關(guān)于內(nèi)存泄漏的問題可看我另一篇文章《解決iOS開發(fā)中app的內(nèi)存泄漏》
10泰演,當界面過于復(fù)雜呻拌,比如短租中的房源詳情,這時候懶加載的tableViewCell可能會引起第一次滑動時界面卡頓現(xiàn)象睦焕,也就是當界面元素過多時懶加載不但不會起到節(jié)省資源藐握,用到時才創(chuàng)建的目的,反而會導(dǎo)致UI資源同一時間大規(guī)模加載垃喊,造成卡頓現(xiàn)象猾普,這對追求極致的開發(fā)體驗來說是非常有影響的,可以用scrollView在viewDidLoad的時候就全部創(chuàng)建出來本谜,這樣才不會卡頓界面初家。
11,NSTimer默認加到runloop的default模式乌助,在跟隨模式下不會執(zhí)行回調(diào)溜在,其設(shè)計理念我認為就是為了使滑動時保持流暢,所以除非必要他托,不要輕易將NSTimer的runllop模式該外common模式掖肋。
12,用instrument調(diào)試后赏参,如果沒有釋放的是imageIO時志笼,注意是因為imageNamed會緩存圖片到內(nèi)存,直到內(nèi)存警告被釋放把篓,如果想用完立馬釋放可以改成imageWithContentsOfFile
13纫溃,NSDateFormatter創(chuàng)建和設(shè)置timeZone,Locale韧掩,dateFormat初始化和設(shè)置屬性很慢紊浩,還有NSCalendar,如果其在for循環(huán)中頻繁創(chuàng)建,將會產(chǎn)生大量的耗時郎楼,可以將其聲明為static單例万伤,防止重復(fù)創(chuàng)建帶來的消耗。
同時NSDateFormatter不是線程安全的呜袁,可將其緩存在線程字典中敌买。
[[NSThread currentThread] threadDictionary]
14,dateFromString方法效率很低阶界,執(zhí)行一萬次方法耗時1100ms虹钮,用strptime的C方法替代,一萬次耗時300ms
time_t t;
struct tm tm;
strptime([iso8601String cStringUsingEncoding:NSUTF8StringEncoding], "%Y-%m-%dT%H:%M:%S%z", &tm);
tm.tm_isdst = -1;
t = mktime(&tm);
[NSDate dateWithTimeIntervalSince1970:t + [[NSTimeZone localTimeZone] secondsFromGMT]];
15,NSFileManager 的attributesOfItemAtPath方法會取文件所有屬性信息膘融,有的可能你根本不需要芙粱,這時可以用stat代替NSFileManager,直接獲取文件屬性:
#import <sys/stat.h>
struct stat statbuf;
const char *cpath = [filePath fileSystemRepresentation];
if (cpath && stat(cpath, &statbuf) == 0) {
NSNumber *fileSize = [NSNumber numberWithUnsignedLongLong:statbuf.st_size];
NSDate *modificationDate = [NSDate dateWithTimeIntervalSince1970:statbuf.st_mtime];
NSDate *creationDate = [NSDate dateWithTimeIntervalSince1970:statbuf.st_ctime];
// etc
}
經(jīng)測試氧映,attributesOfItemAtPath執(zhí)行一萬次需要165ms春畔,stat僅需要78ms
16,NSLog寫消息到Apple的系統(tǒng)日志岛都,并且系統(tǒng)會在主線程序列化NSLog的內(nèi)容律姨。即使最新的iOS設(shè)備中,NSLog所花費的輸出時間也是無法忽略的臼疫,所以發(fā)布環(huán)境中盡可能的少用NSLog
推薦:不用NSLog择份,用DLog,在DEBUG環(huán)境下打印烫堤,在Release下不打印或只打印比較嚴重的錯誤荣赶。
17,stringWithFormat和initWithFormat方法不是特別耗性能,但是如果在for循環(huán)中使用的話可以用asprintf的C函數(shù)來提高性能鸽斟。
NSString *firstName = @"Daniel";
NSString *lastName = @"Amitay";
char *buffer;
asprintf(&buffer, "Full name: %s %s", [firstName UTF8String], [lastName UTF8String]);
NSString *fullName = [NSString stringWithCString:buffer encoding:NSUTF8StringEncoding];
free(buffer);
測試發(fā)現(xiàn)拔创,stringWithFormat執(zhí)行一萬次代碼需要52ms,asprintf僅需19ms
18湾盗,相信很多開發(fā)時間較長的人都遇到過不知不覺app突然push伏蚊,pop動畫丟失了,這個問題大多跟UI操作在子線程執(zhí)行有關(guān)格粪,用UI/DataSource主線程監(jiān)測工具可以保證UI和DataSource操作都在主線程執(zhí)行躏吊。該工具還可以解決像UI響應(yīng)特別慢的操作(由于在子線程操作了UI)
19,Crash監(jiān)測及修復(fù):iOS中crash有三種帐萎,Mach異常比伏,Unix signal信號,NSException疆导,分別可以用Mach API赁项,POSIX API,NSUncaughtExceptionHandler來捕獲,
閃退原因:(1)數(shù)據(jù)庫損壞悠菜,文件損壞舰攒,網(wǎng)絡(luò)返回數(shù)據(jù)異常,代碼bug悔醋,對于數(shù)據(jù)庫摩窃,文件可以刪除,對于網(wǎng)絡(luò)數(shù)據(jù)異常芬骄,代碼bug要分析crash案例猾愿,通過JSPatch來修復(fù)。
20账阻,JSPatch原理:通過蘋果的JavaScriptCore.framework作為引擎蒂秘,是蘋果官方支持的實現(xiàn)在線更新iOS應(yīng)用的庫,其原理是網(wǎng)絡(luò)下載js文件淘太,然后調(diào)用js文件姻僧,通過OC的runtime機制,動態(tài)創(chuàng)建類琴儿,對象段化,替換方法等方式實現(xiàn)運行時修改某一個方法執(zhí)行的代碼,達到修復(fù)bug或動態(tài)更新的目的造成。為了能第一時間發(fā)現(xiàn)程序問題,應(yīng)用程序需要實現(xiàn)自己的崩潰日志收集服務(wù)雄嚣,SDK有KSCrash晒屎,plcrashreporter,CrashKit
21缓升,方法內(nèi)部執(zhí)行大量代碼用AutoreleasePool給drain一下
22鼓鲁,與短租類app類似的可上傳很多張圖片的功能要注意,圖片先存本地港谊,上傳時從本地讀二進制流傳給服務(wù)器骇吭,不要把圖片放到內(nèi)存中上傳,否則占用大量內(nèi)存歧寺。甚至內(nèi)存警告燥狰,無法釋放時crash。
23斜筐,如果用xib開發(fā)的話要注意龙致,多個view不要寫在一個xib文件中,因為即使你不會馬上用到顷链,也會立即加載目代,浪費了寶貴的內(nèi)存資源。
24,GCD使用的時候要注意榛了,它是蘋果提供的自動管理線程的API在讶,但是他的線程數(shù)量是不可控的,所以更精細的線程管理用NSOperationQueue來管理吧霜大,設(shè)置最大并發(fā)數(shù)量真朗。
25,UIView的frame應(yīng)該設(shè)置為整數(shù),否則會觸發(fā)反鋸齒降低性能僧诚,也有可能引起圖形界面的邊界模糊遮婶。
26,layer的drawsAsynchronously屬性會導(dǎo)致layer的CGContext延遲到后臺線程繪制。這個屬性對于頻繁繪制的layer有很大的好處湖笨。
27旗扑,建議陰影操作用圖片的方式來實現(xiàn),但如有必要用layer的陰影屬性時慈省,推薦用shadowPath屬性臀防,系統(tǒng)將會緩存陰影減少不必要的重繪。但當改變layer的bounds時边败,一定要重設(shè)shadowPath袱衷。
CALayer *layer = view.layer;
layer.shadowOpacity = 0.5f;
layer.shadowRadius = 10.0f;
layer.shadowOffset = CGSizeMake(0.0f, 10.0f);
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRect:layer.bounds];
layer.shadowPath = bezierPath.CGPath;
28,通過Storyboard創(chuàng)建視圖對象時笑窜,其資源消耗會比直接通過代碼創(chuàng)建對象要大非常多致燥,在性能敏感的界面里,storyboard不是一個好的技術(shù)選擇排截。
29嫌蚤,CALayer內(nèi)部并沒有屬性,當調(diào)用屬性方法時断傲,背部是通過運行時resolveInstanceMethod為對象臨時添加一個方法脱吱,把對應(yīng)屬性值保存到內(nèi)部一個字典中,還會通知delegate认罩,箱蝠,創(chuàng)建動畫等,非常耗資源垦垂。UIView的關(guān)于顯示的屬性(frame宦搬,bounds,transform)等實際都是ACLayer屬性映射來的乔外,所以對UIView的這些屬性進行調(diào)整時床三,消耗的資源要遠大于一般屬性。因此杨幼,要減少不必要的屬性修改撇簿。
30聂渊,對象的銷毀雖然耗費資源不多,但積累起來也是不容忽視的四瘫。通常當容器類持有大量對象時汉嗽,其銷毀時的資源消耗就非常明顯。如果對象可以放到后臺線程去釋放找蜜,就放到后臺線程饼暑。這里有個小Tip:把對象捕獲到block中,然后扔到后臺隊列去隨便發(fā)個消息洗做,讓其在后臺線程創(chuàng)建一個自動釋放對象弓叛,就會在后臺線程銷毀了。
NSArray *tmp = self.array;
self.array = nil;
dispatch_async(queue, ^{
[tmp class];
});
31诚纸,視圖布局的計算是app中最為常見的消耗CPU資源的地方撰筷。如果能在后臺線程提前計算好視圖布局,并對視圖布局進行緩存畦徘,這個地方基本就不會產(chǎn)生性能問題了毕籽。不論通過何種技術(shù)對視圖進行布局,其最終都會落到對view.frame,bounds,center等屬性的調(diào)整上井辆。上面也說過关筒,這些屬性調(diào)整非常好資源,所以盡量提前計算好布局杯缺,需要時一次性調(diào)整好對應(yīng)屬性蒸播,而不要多次,頻繁的計算和調(diào)整這些屬性夺谁。
32廉赔,AutoLayout是蘋果本身提倡的快速布局的技術(shù),大部分情況下能很好的提升開發(fā)效率匾鸥,但是Autolayout對復(fù)雜視圖來說,會產(chǎn)生嚴重的性能問題碉纳,隨著視圖數(shù)量的增加勿负,AutoLayout帶來的CPU消耗會呈指數(shù)級上升,具體看文章:http://pilky.me/36/劳曹,可以用left奴愉,right,top铁孵,bottom锭硼,width,height快捷屬性蜕劝,或者ComponentKit檀头,AsyncDisplayKit等框架轰异。
33,文本渲染,屏幕上所能看到的所有文本內(nèi)容控件暑始,包括UIWebView搭独,在底層都是通過CoreText排版、繪制為Bitmap顯示的廊镜。常見的文本控件(UILabel牙肝,UITextView等),其排版和繪制都是在主線程進行的嗤朴,當顯示大量文本時配椭,CPU壓力非常大。所以需要自定義文本控件雹姊。用TextKit或底層的CoreText對文本異步繪制股缸,盡管實現(xiàn)麻煩,但帶來的優(yōu)勢也很大容为,CoreText對象創(chuàng)建后乓序,能直接獲取文本的寬高,避免了多次計算坎背,(調(diào)整UILabel的寬度時算一遍替劈,UILabel繪制時再算一遍),CoreText對象占用內(nèi)存較少得滤,可以緩存下來以備多次渲染使用陨献。
34,圖片的解碼懂更,當使用UIImage或CGImageSource的幾個方法創(chuàng)建圖片時眨业,圖片數(shù)據(jù)并不會立刻解碼,設(shè)置到UIImageView或者CALyer提交到GPU前沮协,CGImage中的數(shù)據(jù)才會被解碼龄捡。這一步是發(fā)生在主線程,并且不可避免慷暂,如果要繞開這個機制聘殖,常見的做法是在后臺線程把圖片繪制到CGBitmapContext中,然后從Bitmap直接創(chuàng)建圖片行瑞。目前常見的網(wǎng)絡(luò)圖片庫都自帶這個功能奸腺。
35,圖像的繪制通常是指用CG開頭的方法把圖像繪制到畫布中血久,然后從畫布創(chuàng)建圖片突照,由于CoreGraphic方法通常都是線程安全的,所以圖像繪制可以很容易的放到后臺線程進行氧吐。
例如:
- (void)display {
dispatch_async(backgroundQueue, ^{
CGContextRef ctx = CGBitmapContextCreate(...);
// draw in context...
CGImageRef img = CGBitmapContextCreateImage(ctx);
CFRelease(ctx);
dispatch_async(mainQueue, ^{
layer.contents = img;
});
});
}
36讹蘑,所有的Bitmap末盔,包括圖片,文本衔肢,柵格化的內(nèi)容庄岖,最終都要由內(nèi)存提交給現(xiàn)存,綁定為GPU Texture角骤。不論是提交到顯存的過程隅忿,還是GPU調(diào)整渲染Texture的過程,都要消耗不少GPU資源邦尊,當較短時間顯示大量圖片時(如TableView存在非常多的圖片并且快速滑動時)背桐,CPU占用率很低,GPU占用率非常高蝉揍,界面仍然會掉幀链峭,避免掉幀只能盡量減少短時間內(nèi)大量圖片顯示,或?qū)⒍鄰垐D片合成一張顯示又沾,以及單張圖片像素不能過大弊仪。
37,當多個視圖(或者說layer)重疊在一起顯示時杖刷,GPU會首先把他們混合到一起励饵。如果視圖結(jié)構(gòu)過于復(fù)雜,混合的過程也會消耗很多GPU資源滑燃。為了減輕這種情況的GPU消耗役听,應(yīng)用應(yīng)當盡量減少視圖數(shù)量和層次,并在不透明視圖里表明opaque屬性以避免無用的alpha通道合成表窘。當然典予,也可以用上面的方法,把多個視圖預(yù)先渲染為一張圖片來顯示乐严。
38瘤袖,圖形的生成:CALayer的border,圓角昂验,陰影孽椰,遮罩,CGSharpLayer的矢量圖形的顯示凛篙,通常會觸發(fā)離屏渲染,而離屏渲染通常發(fā)生在GPU中栏渺。當一個列表視圖中出現(xiàn)大量圓角的layer呛梆,并且快速滑動時,可以觀察到GPU資源已經(jīng)占滿磕诊,而CPU資源消耗很少填物。這時候界面仍能正澄齐纾滑動,但是平均幀會降到很低滞磺。對于圓角場合升薯,可以用一個圓角圖片覆蓋到原視圖上模擬同樣的視覺效果。但最根本的解決辦法是把圖片在后臺線程裁剪成圓角击困,圖片上直接加陰影涎劈,圖片方式實現(xiàn)遮罩。