iOS 應(yīng)用性能調(diào)優(yōu)(進階)

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)遮罩。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末阅茶,一起剝皮案震驚了整個濱河市蛛枚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌脸哀,老刑警劉巖蹦浦,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異撞蜂,居然都是意外死亡盲镶,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門蝌诡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來溉贿,“玉大人,你說我怎么就攤上這事送漠⊥缯眨” “怎么了?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵闽寡,是天一觀的道長代兵。 經(jīng)常有香客問我,道長爷狈,這世上最難降的妖魔是什么植影? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮涎永,結(jié)果婚禮上思币,老公的妹妹穿的比我還像新娘。我一直安慰自己羡微,他們只是感情好谷饿,可當我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著妈倔,像睡著了一般博投。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上盯蝴,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天毅哗,我揣著相機與錄音听怕,去河邊找鬼。 笑死虑绵,一個胖子當著我的面吹牛尿瞭,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播翅睛,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼声搁,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了宏所?” 一聲冷哼從身側(cè)響起酥艳,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎爬骤,沒想到半個月后充石,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡霞玄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年骤铃,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片坷剧。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡惰爬,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出惫企,到底是詐尸還是另有隱情撕瞧,我是刑警寧澤,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布狞尔,位于F島的核電站丛版,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏偏序。R本人自食惡果不足惜页畦,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一不见、第九天 我趴在偏房一處隱蔽的房頂上張望棚赔。 院中可真熱鬧,春花似錦摔桦、人聲如沸端朵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽冲呢。三九已至栓撞,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背瓤湘。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留恩尾,地道東北人弛说。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像翰意,于是被迫代替她去往敵國和親木人。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,465評論 2 348

推薦閱讀更多精彩內(nèi)容