首先幔亥,開(kāi)發(fā)過(guò)程中,遇到過(guò)的內(nèi)存的問(wèn)題
1察纯、self被delegate強(qiáng)引用了帕棉,導(dǎo)致內(nèi)存不被釋放。
2饼记、imageNamed讀取圖片的方法香伴,會(huì)緩存在內(nèi)存中,所以較大的圖片具则,還是用imageWithContentsOfFile即纲。
3、block里沒(méi)有使用弱引用self博肋,導(dǎo)致一直持有self得不到釋放低斋。
一、內(nèi)存優(yōu)化
1匪凡、多使用懶加載:不要一次創(chuàng)建所有的subview膊畴,而是當(dāng)需要時(shí)才創(chuàng)建,當(dāng)它們完成了使命病游,把他們放進(jìn)一個(gè)可重用的隊(duì)列中唇跨,避免了不劃算的內(nèi)存分配。
2衬衬、使用復(fù)用:內(nèi)存上對(duì)對(duì)象的復(fù)用轻绞,UITableView就是例子。另外佣耐,一些objects的初始化很慢政勃,比如NSDateFormatter和NSCalendar,想要避免使用這個(gè)對(duì)象的瓶頸就需要重用他們兼砖,可以通過(guò)添加屬性到你的class里或者創(chuàng)建靜態(tài)變量來(lái)實(shí)現(xiàn)奸远。
3、使用xib和storyboard布局注意簡(jiǎn)潔化:因?yàn)榧虞d一個(gè)xib的時(shí)候所有內(nèi)容都被放在了內(nèi)存里讽挟,包括任何圖片懒叛。嘗試為每個(gè)Controller配置一個(gè)單獨(dú)的xib,盡可能把一個(gè)View Controller的view層次結(jié)構(gòu)分散到單獨(dú)的xib中去耽梅。如果有一個(gè)不會(huì)即刻用到的view薛窥,你這就是在浪費(fèi)寶貴的內(nèi)存資源了。StoryBoard中不允許有單個(gè)view的存在,因此很多時(shí)候我們需要借助于單個(gè)的xib來(lái)自定義UI诅迷。
4佩番、選擇正確的數(shù)據(jù)結(jié)構(gòu):精心選擇的數(shù)據(jù)結(jié)構(gòu)可以帶來(lái)更高的運(yùn)行或者存儲(chǔ)效率。
Arrays: 有序的一組值罢杉。使用index查詢(xún)很快趟畏,使用value查表很慢,插入/刪除很慢滩租。
Dictionaries: 存儲(chǔ)鍵值對(duì)赋秀,用鍵來(lái)查找比較快。
Sets: 無(wú)序的一組值律想,用值來(lái)查找很快猎莲,插入/刪除很快。
5技即、由于后臺(tái)運(yùn)行的原因益眉,導(dǎo)致的寫(xiě)代碼時(shí)要注意的:由于在后臺(tái)時(shí),為了減少程序占用的內(nèi)存姥份,系統(tǒng)會(huì)自動(dòng)在回收一些系統(tǒng)幫助你開(kāi)辟的內(nèi)存。
所以:
a.盡量減少內(nèi)存的使用年碘。當(dāng)內(nèi)存不足時(shí)澈歉,iOS將kill那些消耗內(nèi)存最多的 App。
b.釋放所有的共享資源屿衅,比如 Calendar 與 Address book埃难。當(dāng)應(yīng)用程序進(jìn)入后臺(tái)時(shí),如果它還在使用或沒(méi)有釋放共享資源涤久,iOS會(huì)立即kill掉該應(yīng)用程序涡尘。
c.對(duì)于圖片對(duì)象、可以重新加載的大的視頻或數(shù)據(jù)文件和任何沒(méi)用而且可以輕易創(chuàng)建的對(duì)象應(yīng)該盡快的去掉強(qiáng)引用响迂。
d.正確處理App生命周期事件考抄。當(dāng)進(jìn)入后臺(tái)時(shí),應(yīng)該保持應(yīng)用程序數(shù)據(jù)蔗彤,以便回到前臺(tái)時(shí)能夠恢復(fù)川梅。當(dāng)進(jìn)入 inactive 狀態(tài)時(shí),應(yīng)該暫停當(dāng)前的業(yè)務(wù)流然遏。iOS運(yùn)行App在后臺(tái)運(yùn)行的時(shí)間有限贫途,因此后臺(tái)代碼不應(yīng)該執(zhí)行非常耗時(shí)的任務(wù),可能的話(huà)就使用多線(xiàn)程待侵。當(dāng)進(jìn)入后臺(tái)時(shí)丢早,iOS會(huì)保存當(dāng)前App的一個(gè)快照,以便之后在合適的時(shí)候(裝載view和數(shù)據(jù)時(shí))呈現(xiàn)給用戶(hù)以提高用戶(hù)體驗(yàn)秧倾,因此在進(jìn)入后臺(tái)時(shí)怨酝,應(yīng)該避免在屏幕上呈現(xiàn)用戶(hù)信息傀缩,以免泄露用戶(hù)個(gè)人資料。
e.不要更新UI或者執(zhí)行大量消耗CPU或電池的代碼凫碌。進(jìn)入后臺(tái)之后扑毡,不應(yīng)該執(zhí)行不必要的任務(wù),不要執(zhí)行 OpenGL ES 調(diào)用盛险,應(yīng)取消 Bonjour 相關(guān)的服務(wù)瞄摊,正確處理網(wǎng)絡(luò)鏈接失敗,避免更新 UI苦掘,清除所有的警告或其他彈出對(duì)話(huà)框换帜。
f.在后臺(tái)時(shí)正確響應(yīng)系統(tǒng)變化。 如:
設(shè)備旋轉(zhuǎn)消息 UIDeviceOrientationDidChangeNotification
重要的時(shí)間變化(新的一天開(kāi)始或時(shí)區(qū)變化)UIApplicationSignificantTimeChangeNotification
電池變化 UIDeviceBatteryLevelDidChangeNotification 和 UIDeviceBatteryStateDidChangeNotification
用戶(hù)默認(rèn)設(shè)置變化 NSUserDefaultsDidChangeNotification
本地化語(yǔ)言變化 NSCurrentLocaleDidChangeNotification 等鹤啡。
g.保存用戶(hù)數(shù)據(jù)或狀態(tài)信息惯驼,所有沒(méi)寫(xiě)到磁盤(pán)的文件或信息,在進(jìn)入后臺(tái)時(shí)递瑰,最好都寫(xiě)到磁盤(pán)去祟牲,因?yàn)槌绦蚩赡茉诤笈_(tái)被殺死。
applicationDidEnterBackgound: 方法有大概5秒的時(shí)間讓你完成自己的任務(wù)抖部。如果超過(guò)時(shí)間還有未完成的任務(wù)说贝,你的程序就會(huì)被終止而且從內(nèi)存中清除。如果還需要長(zhǎng)時(shí)間的運(yùn)行任務(wù)慎颗,可以調(diào)用 beginBackgroundTaskWithExpirationHandler方法去請(qǐng)求后臺(tái)運(yùn)行時(shí)間和啟動(dòng)線(xiàn)程來(lái)運(yùn)行長(zhǎng)時(shí)間運(yùn)行的任務(wù)乡恕。
6、處理內(nèi)存警告:iOS下每個(gè)app可用的內(nèi)存是被限制的俯萎,如果一個(gè)app使用的內(nèi)存超過(guò)了這個(gè)閥值傲宜,則系統(tǒng)會(huì)向該app發(fā)送Memory Warning消息。收到消息后夫啊,app必須盡可能多的釋放一些不必要的內(nèi)存函卒,否則OS會(huì)關(guān)閉app。最佳方式是移除緩存撇眯、圖片對(duì)象和其他一些可以重創(chuàng)建對(duì)象的強(qiáng)引用谆趾。
處理內(nèi)存警告具體方法如下:
-(void)didReceiveMemoryWarning{[superdidReceiveMemoryWarning];//即使沒(méi)有顯示在window上,也不會(huì)自動(dòng)的將self.view釋放叛本。// 保證是在6.0下使用的,6.0以前屏蔽以下代碼沪蓬,否則會(huì)在下面使用self.view時(shí)自動(dòng)加載viewDidUnLoadif([[UIDevicecurrentDevice].systemVersion floatValue] >=6.0) {//需要注意的是self.isViewLoaded是必不可少的,其他方式訪(fǎng)問(wèn)視圖會(huì)導(dǎo)致它加載if(self.isViewLoaded && !self.view.window)// 是否是正在使用的視圖{self.view =nil;// 目的是再次進(jìn)入時(shí)能夠重新加載調(diào)用viewDidLoad函數(shù)来候。}}}
二跷叉、緩存優(yōu)化:
1、iOS緩存優(yōu)化
盡量 cache 那些可重復(fù)利用的對(duì)象,緩存那些不大可能改變但是需要經(jīng)常讀取的東西云挟,比如table cell梆砸、date/number formatters、遠(yuǎn)端服務(wù)器的響應(yīng)园欣、圖片帖世、甚至計(jì)算結(jié)果(UITableView的行高)等。
2沸枯、NSURLConnection
NSURLConnection默認(rèn)會(huì)緩存資源在內(nèi)存或者存儲(chǔ)中根據(jù)它所加載的HTTP Headers日矫。
3、NSURLCache
iOS中緩存技術(shù)用到了NSURLCache類(lèi)绑榴。
4哪轿、NSCache
NSCache是蘋(píng)果官方提供的緩存類(lèi),NSCache是線(xiàn)程安全的翔怎,在多線(xiàn)程操作中窃诉,不需要對(duì)Cache加鎖。NSCache的Key只是對(duì)對(duì)象的strong引用赤套,對(duì)象不需要實(shí)現(xiàn)NSCopying協(xié)議飘痛,NSCache也不會(huì)像NSDictionary一樣復(fù)制對(duì)象。
5容握、兩種圖片加載方式
常見(jiàn)的從bundle中加載圖片的方式有兩種宣脉,一個(gè)是用imageNamed,二是用imageWithContentsOfFile唯沮。
imageNamed的優(yōu)點(diǎn)是當(dāng)加載時(shí)會(huì)緩存圖片,這個(gè)方法用堪遂,如果圖片存在的話(huà)介蛉,一個(gè)指定的名字在系統(tǒng)緩存中查找并返回一個(gè)圖片對(duì);如果緩存中沒(méi)有找到相應(yīng)的圖片溶褪,這個(gè)方法從指定的文檔中加載然后緩存并返回這個(gè)對(duì)象币旧。而imageWithContentsOfFile僅加載圖片。如果你要加載一個(gè)大圖片而且是一次性使用猿妈,那么就沒(méi)必要緩存這個(gè)圖片吹菱,用imageWithContentsOfFile足矣,這樣不會(huì)浪費(fèi)內(nèi)存來(lái)緩存它彭则。然而鳍刷,在圖片反復(fù)重用的情況下imageNamed是一個(gè)好得多的選擇。
三俯抖、CPU緩存解析
四输瓜、iOS存儲(chǔ)優(yōu)化
使用最合適的數(shù)據(jù)存儲(chǔ)方式
NSUerDefaults:便捷,但是只適用于小數(shù)據(jù);
XML, JSON, 或者 plist:能處理大數(shù)據(jù)尤揣,但需要讀取整個(gè)文件到內(nèi)存里去解析搔啊,浪費(fèi)資源;
NSCoding存檔:也需要讀取文件北戏,具有同上的問(wèn)題负芋;
SQL數(shù)據(jù)庫(kù)或者 Core Data:使用這些技術(shù)比較好,用特定的查詢(xún)語(yǔ)句就能只加載需要的對(duì)象嗜愈。
1)旧蛾、Core Data使用分析:
a.概念:Core Date是對(duì)SQLite的封裝,提供了更高級(jí)的數(shù)據(jù)持久化解決方案芝硬。Core Data是完全獨(dú)立于任何UI層級(jí)的框架蚜点,它是作為模型層框架被設(shè)計(jì)出來(lái)的。在對(duì)數(shù)據(jù)庫(kù)操作時(shí)拌阴,不需要使用sql語(yǔ)句绍绘,是一種ORM(“對(duì)象關(guān)系映射”)的數(shù)據(jù)庫(kù)操作方式,ORM將關(guān)系數(shù)據(jù)庫(kù)中的表迟赃,轉(zhuǎn)化為程序中的對(duì)象陪拘,但實(shí)際上是對(duì)數(shù)據(jù)中的數(shù)據(jù)進(jìn)行操作,按照面向?qū)ο蟮乃枷胂吮冢褂脤?shí)體模型來(lái)操作數(shù)據(jù)庫(kù)左刽。使用Core Data進(jìn)行數(shù)據(jù)庫(kù)存取不需要手動(dòng)創(chuàng)建數(shù)據(jù)庫(kù),創(chuàng)建數(shù)據(jù)庫(kù)的過(guò)程完全由Core Data框架自動(dòng)完成酌媒,開(kāi)發(fā)者需要做的就是把模型創(chuàng)建起來(lái)欠痴,Core Data實(shí)際上是將數(shù)據(jù)庫(kù)的創(chuàng)建、表的創(chuàng)建秒咨、對(duì)象和表的轉(zhuǎn)換等操作封裝起來(lái)喇辽,以簡(jiǎn)化我們的操作。如果模型發(fā)生了變化雨席,可以選擇重新生成實(shí)體類(lèi)文件菩咨,但是自動(dòng)生成的數(shù)據(jù)庫(kù)并不會(huì)自動(dòng)更新,需要考慮重新生成數(shù)據(jù)庫(kù)陡厘,并把之前數(shù)據(jù)庫(kù)中數(shù)據(jù)進(jìn)行移植抽米。
b.優(yōu)勢(shì):簡(jiǎn)化操作。(特別是在A(yíng)pp升級(jí)之后數(shù)據(jù)庫(kù)字段或者表有更改會(huì)導(dǎo)致crash)
c.劣勢(shì):不支持跨平臺(tái)使用糙置。
2)云茸、SQL數(shù)據(jù)庫(kù)使用分析:
a.概念:Core Data代表一個(gè)對(duì)象的graph model(圖表模型),SQLite就是一個(gè)DBMS谤饭,也更加底層查辩。使用SQLite胖笛,可以用FMDB這個(gè)庫(kù)來(lái)簡(jiǎn)化SQLite的操作栅盲,節(jié)省了了解SQLite的C API的時(shí)間芋类。
b.優(yōu)勢(shì):支持跨平臺(tái)使用
c.劣勢(shì):相對(duì)Core Data操作復(fù)雜
五、網(wǎng)絡(luò)請(qǐng)求優(yōu)化
1)霍弹、對(duì)請(qǐng)求壓縮:減小文檔的一個(gè)方式就是在服務(wù)端和你的app中打開(kāi)gzip萍倡,這對(duì)于文字這種能有更高壓縮率的數(shù)據(jù)來(lái)說(shuō)會(huì)有更顯著的效用身弊。iOS已經(jīng)在NSURLConnection中默認(rèn)支持了gzip壓縮,當(dāng)然AFNetworking這些基于它的框架亦然列敲。
2)阱佛、對(duì)數(shù)據(jù)壓縮:Web瀏覽器對(duì)請(qǐng)求壓縮的支持并不太好,因?yàn)闉g覽器不知道目標(biāo)服務(wù)器是否能夠支持對(duì)請(qǐng)求的解壓縮戴而。所以Base64預(yù)先壓縮數(shù)據(jù)是個(gè)很好的方法凑术,比如以Base64格式上傳JPEG文件,那么可以對(duì)Base64數(shù)據(jù)進(jìn)行壓縮所意,相較于未壓縮的Base64數(shù)據(jù)淮逊,壓縮后的數(shù)據(jù)體積會(huì)降低30%左右。
3)扶踊、選擇合適的傳輸數(shù)據(jù)的方案:壓縮模式的效率在很大程序上取決于待壓縮的數(shù)據(jù)泄鹏,不過(guò)通常情況下JSON都是一種更為高效的模式。
4)秧耗、降低請(qǐng)求延遲:有兩項(xiàng)最佳實(shí)踐:
a.在單個(gè)TCP連接上發(fā)送HTTP請(qǐng)求备籽,以管道的形式發(fā)送HTTP請(qǐng)求,從而優(yōu)化全雙工TCP連接的使用分井。Apache和IIS都支持管道车猬,無(wú)需任何額外的配置。
b.通過(guò)HTTP緩存機(jī)制的基本原理尺锚,在iOS應(yīng)用中利用這些規(guī)則珠闰,可以在本地緩存內(nèi)容以避免不必要的網(wǎng)絡(luò)流量。如我們上面提到的NSURLCache缩麸、NSCache等铸磅。
5)赡矢、另外杭朱,優(yōu)化網(wǎng)絡(luò)請(qǐng)求的一環(huán)是DNS解析:因?yàn)榭蛻?hù)端app的請(qǐng)求第一步都是DNS解析,但由于cache的存在使得大部分的解析請(qǐng)求并不會(huì)產(chǎn)生任何延遲吹散。所以弧械,如果能直接跳過(guò)DNS解析這一步,當(dāng)然能提升網(wǎng)絡(luò)性能了空民。
a.一種方法是使用DNS映射刃唐。
b.另外就是直接用ip請(qǐng)求數(shù)據(jù)羞迷。DNS解析請(qǐng)求簡(jiǎn)單來(lái)說(shuō),就是輸入一個(gè)域名画饥,輸出一個(gè)ip地址衔瓮。做自己的映射機(jī)制也就是客戶(hù)端本地維護(hù)這樣一個(gè)映射文件,只不過(guò)這個(gè)映射文件需要能從服務(wù)器更新抖甘,還要做一些容錯(cuò)處理热鞍。