昨天我們講解了程序性能優(yōu)化中的內(nèi)存優(yōu)化,今天繼續(xù)跟隨南昌網(wǎng)絡(luò)公司小編學(xué)習(xí)如何優(yōu)化程序性能。重點(diǎn)分析程序性能優(yōu)化之優(yōu)化資源文件,延遲加載和數(shù)據(jù)持久優(yōu)化這三種方法,讓你的程序“飛”起來(lái)。
從狹義上講,資源文件是放置在應(yīng)用程序本地與應(yīng)用程序一起編譯、打包和發(fā)布的非程序代碼文件,如應(yīng)用中用到的聲音、視頻、圖片和文本文件等。從廣義上講,資源文件可以放置于任何地方,既可以放置于本地,也可以放在云服務(wù)器中。
在iOS中,本地資源文件編譯后,會(huì)放置于應(yīng)用程序包文件中(即<應(yīng)用名>.app文件)。如下代碼用于訪問(wèn)如圖1所示team.plist本地資源文件:
圖1 資源文件
圖1所示的“球隊(duì)圖片”組也放置了一些資源文件。添加資源文件的方法是通過(guò)右鍵添加文件到工程中。資源文件在使用的過(guò)程中需要優(yōu)化,包括文件格式、文件類型、文件大小和文件結(jié)構(gòu)等方面,使得它更適合某個(gè)應(yīng)用?!斑m合”這兩個(gè)字很重要。當(dāng)然,優(yōu)化方向有很多,下面我們從圖片文件優(yōu)化和音頻文件優(yōu)化這兩個(gè)方面介紹一下。
1.圖片文件優(yōu)化
圖片文件優(yōu)化包括文件格式和文件大小的優(yōu)化。在移動(dòng)設(shè)備中,支持的圖片格式主要是PNG、GIF和JPEG格式,蘋(píng)果推薦使用PNG格式。在Xcode中,集成了第三方PNG優(yōu)化工具pngcrush①,它可以在編譯的時(shí)候?qū)NG格式文件進(jìn)行優(yōu)化和壓縮,而我們只需要設(shè)定如圖2所示的編譯參數(shù)Compress PNG Files為Yes就可以了。
圖2 設(shè)定編譯參數(shù)Compress PNG Files
打開(kāi)“ ImageFile”工程中“測(cè)試圖片”目錄中background(未優(yōu)化).png文件,在Finder中查看該文件的屬性,它是一個(gè)320×480px、大小為317KB的PNG圖片,如圖3所示。
使用Xcode編譯工程,在編譯之后的目錄中找到ImageFile.app包文件。打開(kāi)包文件,查看目錄中background(未優(yōu)化).png文件的屬性,可以發(fā)現(xiàn)該文件是205KB的PNG圖片了,如圖4所示。
圖3 未優(yōu)化的PNG文件屬性
圖4 優(yōu)化的PNG文件屬性
Xcode工具可以在編譯時(shí)優(yōu)化PNG圖片,但是即便經(jīng)過(guò)優(yōu)化和壓縮的PNG圖片文件,也比JPEG圖片文件大得多。開(kāi)“ImageFile”工程中“測(cè)試圖片”目錄中的background-8(優(yōu)化壓縮).png文件和background-h.jpg文件,比較可以發(fā)現(xiàn),前者是經(jīng)過(guò)優(yōu)化的質(zhì)量最低的PNG-8(8位PNG格式)文件,其大小是61KB;后者是經(jīng)過(guò)優(yōu)化的質(zhì)量最高的JPEG格式文件,其大小是22KB。在本例中,PNG比JPEG文件大3倍多。
如果是本地資源文件,這樣的差別不是很大,但如果是分布在網(wǎng)絡(luò)云服務(wù)器中的資源文件,應(yīng)用在加載這些圖片時(shí),會(huì)從網(wǎng)絡(luò)上下載到本地,這時(shí)候JPEG就很有優(yōu)勢(shì)了。
綜上所述,如果在本地資源的情況下,我們應(yīng)該優(yōu)先使用PNG格式文件,如果資源來(lái)源于網(wǎng)絡(luò),最好采用JPEG格式文件。
另外,圖片是一種很特殊的資源文件。創(chuàng)建UIImage對(duì)象時(shí),可以使用靜態(tài)工廠方法+imageNamed:和實(shí)例構(gòu)造器-initWithContentsOfFile:。+imageNamed:方法會(huì)在內(nèi)存中建立緩存,這些緩存直到應(yīng)用停止才清除。如果是貫穿整個(gè)應(yīng)用的圖片(如圖標(biāo)、logo等),推薦使用+imageNamed:創(chuàng)建。如果是僅使用一次的圖片,推薦使用實(shí)例構(gòu)造器-initWithContentsOfFile:創(chuàng)建。
2.音頻文件優(yōu)化
在討論音頻文件優(yōu)化之前,我們先討論一下音頻文件格式。在iOS平臺(tái)中,主要的音頻文件格式有以下4種。
WAV文件。WAV是一種由微軟和IBM聯(lián)合開(kāi)發(fā)的用于音頻數(shù)字存儲(chǔ)的文件格式。WAV文件的格式靈活,
可以存儲(chǔ)多種類型的音頻數(shù)據(jù)。由于文件較大,不太適合于移動(dòng)設(shè)備這些存儲(chǔ)容量小的設(shè)備。
MP3(MPEG Audio Layer 3)文件。MP3利用MPEG Audio Layer 3技術(shù),將數(shù)據(jù)以1∶10甚至1∶12的縮率壓縮成容量較小的文件。MP3是一種有損壓縮格式,它 盡可能地去掉人耳無(wú)法感覺(jué)的部分和不敏感的部分。這么高的壓縮比率非常適合于移動(dòng)設(shè)備這些存儲(chǔ)容量小的設(shè)備,現(xiàn)在非常流行。
CAFF(Core Audio File Format)文件。CAFF是蘋(píng)果開(kāi)發(fā)的專門用于Mac OS X和iOS系統(tǒng)的無(wú)壓縮音頻格式,它被設(shè)計(jì)用來(lái)替換老的WAV格式。
AIFF(Audio Interchange File Format)文件。AIFF是蘋(píng)果開(kāi)發(fā)的專門用于Mac OS X系統(tǒng)的專業(yè)的音頻文件格式。AIFF的壓縮格式是AIFF-C(或AIFC),將數(shù)據(jù)以4∶1壓縮率進(jìn)行壓縮,應(yīng)用于Mac OS X和iOS系統(tǒng)。
音頻文件優(yōu)化包括了文件格式和文件大小的優(yōu)化,但也要考慮到文件使用場(chǎng)景、采用的技術(shù)(OpenAL、AVAudioPlayer)等因素。在iOS應(yīng)用中,使用本地音頻資源文件的主要應(yīng)用場(chǎng)景是背景音樂(lè)和音樂(lè)特效,下面我們從這兩個(gè)方面介紹相關(guān)的優(yōu)化技術(shù)。
(1)背景音樂(lè)優(yōu)化
背景音樂(lè)會(huì)在應(yīng)用中反復(fù)播放,它會(huì)一直駐留在內(nèi)存中并耗費(fèi)CPU,所以更合適比較小的文件,而壓縮文件是不錯(cuò)的選擇。壓縮文件主要有AIFC和MP3兩種格式,一般我們首選AIFC,因?yàn)檫@是蘋(píng)果推薦的格式。但是我們獲得的原始文件格式不一定是AIFC,這種情況下我們需要使用afconvert工具將其轉(zhuǎn)換為AIFC格式。在終端中執(zhí)行如下命令:
$ afconvert -f AIFC -d ima4 Fx08822_cast.wav
其中-f AIFC參數(shù)用于轉(zhuǎn)換為AIFC格式,-d ima4參數(shù)指定解碼方式,F(xiàn)x08822_cast.wav是要轉(zhuǎn)換的源文件。轉(zhuǎn)換成功后,會(huì)在相同目錄下生成Fx08822_cast.aifc文件。本例中的源文件Fx08822_cast.wav的大小是295KB,轉(zhuǎn)換之后的Fx08822_cast.aifc文件的大小是82KB。當(dāng)然,afconvert工具也可以轉(zhuǎn)換MP3等其他壓縮格式文件。如果我們同時(shí)有WAV文件,就應(yīng)該優(yōu)先采用WAV文件。MP3本身是有損壓縮,如果再經(jīng)過(guò)afconvert轉(zhuǎn)換,音頻的質(zhì)量會(huì)受到影響。
(2)音樂(lè)特效優(yōu)化
音樂(lè)特效用于很多游戲中,如發(fā)射子彈、敵人被打死或按鈕點(diǎn)擊等發(fā)出的聲音,這些聲音都是比較短的。如果追求震撼的3D效果,可以采用蘋(píng)果專用的無(wú)壓縮CAFF格式文件,其他格式的文件盡量不要考慮。一般不要使用壓縮音頻文件,這主要是因?yàn)橐魳?lè)特效通常采用OpenAL技術(shù),它只接受無(wú)壓縮的音頻文件。另外,壓縮音頻文件都會(huì)造成音質(zhì)的丟失。如果我們沒(méi)有CAFF格式的文件,也可以使用afconvert工具將其轉(zhuǎn)換為CAFF格式。在終端中執(zhí)行如下命令:
$ afconvert -f caff -d LEI16 Fx08822_cast.wav
其中-f caff參數(shù)用于轉(zhuǎn)換為CAFF格式,-d LEI16參數(shù)指定解碼方式,F(xiàn)x08822_cast.wav是要轉(zhuǎn)換的源文件。默認(rèn)音頻的采樣頻率為22050Hz,如果想提高音頻采樣頻率,可以通過(guò)如下命令:
$ afconvert -f caff -d LEI16@44100 Fx08822_cast.wav
其中-d LEI16@44100參數(shù)中的44100表示音頻采用頻率44100Hz。
如果我們采用的資源文件不在本地,而是在分布在網(wǎng)絡(luò)云服務(wù)器中,那么情況就另當(dāng)別論了。應(yīng)用在加載這些音頻文件時(shí),帶寬往往是要考慮的問(wèn)題,減小文件大小勝過(guò)對(duì)音質(zhì)的要求,這種情況下MP3格式是非常適合的。
綜上所述,音頻文件在使用本地資源的情況下,應(yīng)用于背景音樂(lè)時(shí)AIFC格式是首選,應(yīng)用于音樂(lè)特效時(shí)CAFF格式是首選。如果是資源來(lái)源于網(wǎng)絡(luò),最好采用MP3格式文件。
3.延遲加載
延遲加載(lazy load)指一些對(duì)象不是在應(yīng)用和視圖等初始化時(shí)創(chuàng)建,而是在用到它的時(shí)候創(chuàng)建。當(dāng)應(yīng)用中有一些對(duì)象并不經(jīng)常使用時(shí),延遲加載可以提高程序性能。
3.1 資源文件的延遲加載
首先,我們要考慮的就是對(duì)資源文件的延遲加載。由于資源文件的訪問(wèn)涉及IO操作,這本身就會(huì)耗費(fèi)一定的CPU時(shí)間,如果文件比較大而且加載時(shí)機(jī)又不合適,就會(huì)造成內(nèi)存浪費(fèi)。前面我們了解到資源文件包括圖片、音頻和文本文件等,無(wú)論是什么類型的文件,有些情況下采用延遲加載是很有必要的。例如,我們有如圖5所示的需求,可以使用分屏件(UIPageControl)左右滑動(dòng)屏幕來(lái)瀏覽這3張圖片。
5 圖片延遲加載實(shí)例
PageControlNavigation實(shí)例是沒(méi)有采用延遲加載的實(shí)現(xiàn)代碼,其中的ViewController代碼如下:
我們是在viewDidLoad方法中一次加載全部3張圖片,但是有的時(shí)候用戶不一定會(huì)瀏覽后面的圖片,他可能只看到第一張或第二張,后面的第三張沒(méi)有去看,此時(shí)后面的兩張圖片仍然加載內(nèi)存的話,會(huì)造成內(nèi)存浪費(fèi)。采用延遲加載實(shí)現(xiàn)時(shí)(見(jiàn)實(shí)例LazyLoadPageControlNavigation),ViewController的代碼如下:
我們重新修改了這個(gè)實(shí)例,在viewDidLoad方法中只加載第一張圖片,見(jiàn)第①行代碼。如果用戶滑動(dòng)屏幕或點(diǎn)擊分屏控件進(jìn)入第二個(gè)屏幕,則調(diào)用loadImage:方法加載第二張圖片,類似地,如果要進(jìn)入第三個(gè)屏幕,則調(diào)用loadImage:方法加載第三張圖片。
在這兩種實(shí)現(xiàn)方式中,LazyLoadPageControlNavigation實(shí)現(xiàn)了延遲加載。很顯然,LazyLoadPageControlNavigation的延遲加載友好很多。那么,兩者究竟有多大的差別,這是可以量化的。通過(guò)Instruments工具的Allocations模板,可以分析ViewController視圖控制器加載時(shí)內(nèi)存占用方面的差別。圖6是無(wú)延遲加載實(shí)現(xiàn)案例的Allocations模板跟蹤,圖7是采用延遲加載實(shí)現(xiàn)案例的Allocations模板跟蹤。
如圖6所示,界面啟動(dòng)用時(shí),內(nèi)存占用馬上達(dá)到8.12MB。如圖7所示,界面啟動(dòng)用時(shí),內(nèi)存占用3.08MB,當(dāng)我們滑動(dòng)到第二和第三屏幕時(shí),內(nèi)存占用達(dá)到8.06MB,內(nèi)存變化會(huì)有明顯的兩個(gè)階梯。
圖6 無(wú)延遲加載實(shí)現(xiàn)案例的Allocations模板跟蹤
圖7 使用延遲加載實(shí)現(xiàn)案例的Allocations模板跟蹤
在上面的案例中可以發(fā)現(xiàn),延遲加載的優(yōu)勢(shì)很明顯。如果一定會(huì)訪問(wèn)到資源文件,則延遲加載這些資源文件時(shí),內(nèi)存占用方面就沒(méi)有優(yōu)勢(shì)了,但是在界面加載速度方面還是有優(yōu)勢(shì)的。
3.2 故事板文件的延遲加載
xib和故事板也都屬于資源文件,它是非常特殊的資源文件,應(yīng)用不僅需要讀取它,而且要根據(jù)里面描述的信息創(chuàng)建視圖和子視圖,以及它們的視圖控制器等對(duì)象。創(chuàng)建這么多對(duì)象會(huì)耗費(fèi)很多時(shí)間,占用很多內(nèi)存,因此,它們的延遲加載問(wèn)題非常重要。
默認(rèn)情況下,創(chuàng)建基于故事板的應(yīng)用時(shí),只有一個(gè)故事板文件。這種情況下,故事板內(nèi)部的視圖控制器的創(chuàng)建和加載都是由Segue來(lái)控制的,Segue會(huì)幫助我們管理好這些控制器,包括延遲加載等問(wèn)題。
我們創(chuàng)建一個(gè)實(shí)用型應(yīng)用程序①,研究故事板的延遲加載機(jī)理。實(shí)用型應(yīng)用一般會(huì)有兩個(gè)視圖:主視圖,它顯示應(yīng)用的主要功能;子視圖,它用來(lái)對(duì)應(yīng)用進(jìn)行一些設(shè)置。我們自己創(chuàng)建一個(gè)實(shí)用型應(yīng)用,如圖8所示。
圖8 實(shí)用型應(yīng)用
在Xcode 5之前可以使用Utility Application模板創(chuàng)建,Xcode 6之后就沒(méi)有這個(gè)模板了,我們可以通過(guò)SingleView Application模板創(chuàng)建StoryboardLazyLoadDemo工程。
在主視圖中點(diǎn)擊 按鈕,MainViewController會(huì)延遲加載FlipsideViewController,然后彈出模態(tài)模式。使用模態(tài)Segue連接MainViewController和FlipsideViewController,如圖9所示,我們基本上不需要編寫(xiě)什么代碼。
圖9 模態(tài)視圖的Segue
Segue定義了兩個(gè)視圖控制器的導(dǎo)航關(guān)系,也用來(lái)維護(hù)和管理下一個(gè)視圖控制器的延遲加載時(shí)機(jī),這種情況下我們無(wú)法“插手”視圖控制器的延遲加載。但是一種情況除外,那就是使用了故事板,而控制器之間沒(méi)有定義導(dǎo)航關(guān)系,沒(méi)有定義Segue,如圖10所示。
圖10 沒(méi)有定義Segue的故事板
這種情況下,添加showInfo:方法響應(yīng)主視圖的 按鈕點(diǎn)擊事件,具體可參考StoryboardLazyLoadNoSegueDemo工程的MainViewController,相關(guān)代碼如下:
在單一故事板文件中,第①行代碼可以獲得當(dāng)前的故事板對(duì)象。如果想在多故事板的情況下獲得非當(dāng)前故事板對(duì)象,可以通過(guò)第②行代碼的UIStoryboard構(gòu)造器創(chuàng)建。本例中不用使用該語(yǔ)句,使用它會(huì)多創(chuàng)建一個(gè)故事板對(duì)象,就會(huì)占用更多的內(nèi)存。
3.3 xib 文件的延遲加載
相對(duì)于故事板而言,xib要靈活很多。xib文件有兩種:一種是描述視圖控制器的,另一種是描述視圖的,它們的加載方式有所區(qū)別。無(wú)論是哪一種,分散管理的xib文件使我們通過(guò)編程方式訪問(wèn)它更加方便。
Xcode 6不能創(chuàng)建基于xib文件的工程了,我們通過(guò)Single View Application模板創(chuàng)建NibLazyLoadDemo工程,然后刪除主故事板文件。接著我們創(chuàng)建視圖控制器,如圖11所示,一定要選擇Also create XIB file復(fù)選框,這會(huì)幫助我們創(chuàng)建與視圖控制器對(duì)應(yīng)的xib文件。
圖11 創(chuàng)建視圖控制器
創(chuàng)建好視圖控制器后,我們需要修改AppDelegate使應(yīng)用啟動(dòng)時(shí)能夠加載MainViewController。AppDelegate的主要代碼如下:
上述代碼中,第①~③行是我們添加的代碼,第①行代碼用于創(chuàng)建Window對(duì)象,在xib構(gòu)建的視圖中必須放到一個(gè)Window中,第②行代碼通過(guò)xib文件創(chuàng)建視圖控制器對(duì)象,然后再把視圖控制器添加到Window對(duì)象中。主視圖控制器MainViewController中showInfo:方法的代碼如下:
本例中的xib文件是視圖控制器xib文件,我們可以使用視圖控制器的initWithNibName:bundle:構(gòu)造器從xib文件中創(chuàng)建視圖控制器對(duì)象。
有些情況下,故事板和xib會(huì)混合使用。在有故事板的工程中,有時(shí)候需要使用別人已經(jīng)編寫(xiě)好的xib文件和對(duì)應(yīng)類(視圖或視圖控制器)。當(dāng)然,通過(guò)上面的兩種方式也是可以的。
4.數(shù)據(jù)持久化的優(yōu)化
在iOS中,數(shù)據(jù)持久化的載體主要有文件、SQLite數(shù)據(jù)庫(kù)和Core Data。本節(jié)中,我們就從這幾個(gè)方面入手討論數(shù)據(jù)持久化的優(yōu)化問(wèn)題。
4.1 使用文件
文件是數(shù)據(jù)持久化的重要載體。文件優(yōu)化可以包括很多方面,下面我們從文件訪問(wèn)、文件結(jié)構(gòu)和文件大小這3個(gè)方面來(lái)介紹。
(1)文件訪問(wèn)優(yōu)化
避免多次寫(xiě)入很少的數(shù)據(jù),最好是當(dāng)數(shù)據(jù)積攢到一定數(shù)量時(shí)一次寫(xiě)入。因?yàn)槲募L問(wèn)涉及IO操作,我們知道頻繁的IO操作會(huì)影響性能,所以最好將文件讀寫(xiě)訪問(wèn)從主線程中剝離出來(lái),由一個(gè)子線程負(fù)責(zé)。另外,過(guò)于頻繁地寫(xiě)入數(shù)據(jù)會(huì)影響設(shè)備中閃存的壽命。
文件的寫(xiě)入應(yīng)該采用增量方式,每次只寫(xiě)入變化的部分,不要為改變幾個(gè)字節(jié)寫(xiě)入整個(gè)文件。這樣就要求不能采用簡(jiǎn)單的屬性列表對(duì)象寫(xiě)入方式。這是一個(gè)很復(fù)雜的問(wèn)題,文件內(nèi)容的變化可以是追加、刪除和修改。文件追加很容易實(shí)現(xiàn),刪除就比較麻煩了,需要找到要?jiǎng)h除的數(shù)據(jù),這樣訪問(wèn)文件就采用隨機(jī)訪問(wèn)方式了。修改與刪除的問(wèn)題是一樣的。與其這么麻煩,不如采用別的持久化技術(shù)了。
(2)文件結(jié)構(gòu)優(yōu)化
文件要保存數(shù)據(jù),它就應(yīng)該是結(jié)構(gòu)化的。蘋(píng)果中的.plist文件就是很好的結(jié)構(gòu)化文件,其結(jié)構(gòu)是層次模型的樹(shù)形結(jié)構(gòu),層次的深淺會(huì)影響讀取/寫(xiě)入的速度。在能夠滿足用戶需求的情況下,要減少層次深度。下面是一個(gè)世界杯足球賽部分小組信息的屬性列表文件team(5層次).plist:
該文件有5個(gè)層次,具體如圖12所示,其中第一層是數(shù)組類型集合;第二層是字典集合,其中描述了小組名和小組中的球隊(duì)列表;第三層是數(shù)組類型集合,描述了小組中的球隊(duì)列表;第四層是字典集合;第五層是字符串,描述了球隊(duì)名和球隊(duì)圖標(biāo)信息。
圖12 5個(gè)層次的team.plist文件
這個(gè)文件訪問(wèn)起來(lái)很不方便,遍歷起來(lái)也很不方便,也很影響性能。我們重新設(shè)計(jì)了這個(gè)屬性列表文件,其內(nèi)容如下:
此時(shí)這個(gè)文件有3個(gè)層次,其中第一層是數(shù)組類型集合,第二層是字典集合,第三層是字符串,描述了球隊(duì)名和球隊(duì)圖標(biāo)信息,如圖13所示。
圖13 3層次的team.plist文件
與上面的5層次文件相比,3層次訪問(wèn)起來(lái)比較方便,性能會(huì)比較好。此外,在文件大小方面,3層次文件是647KB,5層次文件是893KB。
(3)文件大小優(yōu)化
文件大小也是優(yōu)化的一個(gè)重要指標(biāo)。從上面的比較可以看到,調(diào)整文件結(jié)構(gòu)可以減少文件大小。此外,我們也可以通過(guò)序列化.plist文件減少文件大小。 Foundation框架提供了NSPropertyListSerialization類,它就是為此而設(shè)計(jì)的。NSPropertyListSerialization類中有2個(gè)常用方法,具體如下所示。
+ dataWithPropertyList:format:options:error:。按照指定的格式和操作參數(shù),序列化屬性列表對(duì)象到NSData對(duì)象。
+ propertyListWithData:options:format:error:。按照指定的格式和操作參數(shù),從NSData對(duì)象反序列化到屬性列表對(duì)象中。
為了介紹NSPropertyListSerialization類,現(xiàn)在我們換成序列化二進(jìn)制文件NotesList.binary。下面我們修改數(shù)據(jù)持久層工程PersistenceLayer中的NoteDAO類,首先,添加如下兩個(gè)方法:
在上述代碼中,readFromArray:方法從文件中讀取數(shù)據(jù)到NSMutableArray,其流程是讀取文件到NSMutableData對(duì)象,然后再?gòu)腘SMutableData對(duì)象中反序列化處理屬性列表對(duì)象。本例中的屬性列表對(duì)象是NSMutableArray類型,其中第①行代碼用于處理這一過(guò)程。propertyListWithData后面的參數(shù)是反序列化的數(shù)據(jù)來(lái)源,它是NSData類型。options后面的參數(shù)是NSPropertyListReadOptions。在Swift版本中,使用表達(dá)式NSPropertyListReadOptions-(NSPropertyListMutabilityOptions.MutableContainers.rawValue),Objective-C版本是NSPropertyListMutableContainersAndLeaves。NSPropertyListMutabilityOptions是枚舉類型,其成員值如下。
Immutable。屬性列表包含不可變對(duì)象。Objective-C版本為NSPropertyListImmutable。
MutableContainers。屬性列表父節(jié)點(diǎn)是可變類型,子節(jié)點(diǎn)是不可變類型。Objective-C版本為NSPropertyListMutableContainers。
MutableContainersAndLeaves。屬性列表父節(jié)點(diǎn)和子節(jié)點(diǎn)都是可變類型。Objective-C版本為NSPropertyListMutableContainersAndLeaves。
另外,在第①行代碼中,format參數(shù)為nil(或NULL),說(shuō)明格式是自動(dòng)識(shí)別的。
南昌網(wǎng)絡(luò)公司小編提示:屬性列表對(duì)象是與屬性列表文件結(jié)構(gòu)對(duì)應(yīng)的,它可以是NSData、NSString、NSArray和NSDictionary類型以及它們的可變類型。此外,還可以是NSDate和NSNumber類型。
write:toFilePath:方法把NSMutableArray數(shù)據(jù)序列化后寫(xiě)入到文件中,流程是先序列化NSMutableArray數(shù)據(jù)到 NSData 對(duì)象中,然后在把 NSData 對(duì)象寫(xiě)入到文件中。第②行代碼就是完成序列化處理的,+dataWithPropertyList:format:options:error:方法中array參數(shù)是要序列化的屬性列表對(duì)象,format參數(shù)是NSPropertyListFormat枚舉類型。NSPropertyListFormat枚舉類型包含的常量有如下幾個(gè)。
XMLFormat_v1_0。指定屬性列表文件格式是XML格式,仍然是純文本類型,不會(huì)壓縮文件。Objective-C版本為NSPropertyListXMLFormat_v1_0。
BinaryFormat_v1_0。指定屬性列表文件格式為二進(jìn)制格式,文件是二進(jìn)制類型,會(huì)壓縮文件。Objective-C版本為NSPropertyListBinaryFormat_v1_0。
OpenStepFormat。指定屬性列表文件格式為ASCII碼格式,對(duì)于舊格式的屬性列表文件,不支持寫(xiě)入操作。
Objective-C版本為NSPropertyListOpenStepFormat。
本例中,我們?cè)O(shè)置的是BinaryFormat_v1_0,大小減少了,加載速度提高了,這樣就達(dá)到了優(yōu)化的效果。
4.2 使用 SQLite 數(shù)據(jù)庫(kù)
當(dāng)需要處理較大的數(shù)據(jù)集合時(shí),就不能采用文件了。因?yàn)槲募恢С质聞?wù)處理,這時(shí)候我們可以選擇SQLite數(shù)據(jù)庫(kù)或Core Data。本節(jié)中,我們先從表結(jié)構(gòu)、查詢和插入(或刪除)這幾個(gè)方面介紹一下SQLite數(shù)據(jù)庫(kù)方面的優(yōu)化。
(1)表結(jié)構(gòu)優(yōu)化
SQLite是嵌入式關(guān)系型數(shù)據(jù),它可以建立多表之間復(fù)雜的關(guān)系,但是如果放在iOS、Android等這些移動(dòng)設(shè)備上時(shí),我們需要考慮設(shè)備上本地表能建多少,表中字段有多少,表之間關(guān)系的復(fù)雜程度等問(wèn)題。
在CPU處理能力低、內(nèi)存少、存儲(chǔ)空間少的情況下,我們不能在本地建立復(fù)雜表關(guān)系,表的個(gè)數(shù)不要超過(guò)5個(gè),表中的字段數(shù)也不宜太多。移動(dòng)設(shè)備中的數(shù)據(jù)不可能是企業(yè)級(jí)系統(tǒng)數(shù)據(jù)的全部,它只是企業(yè)級(jí)系統(tǒng)的補(bǔ)充和擴(kuò)展。例如,在你的iPhone手機(jī)中,不可能有全部的新浪微博用戶信息,一方面是不安全,另一方面是數(shù)據(jù)量很大,最高配置的iPhone也不可能存放下這么多數(shù)據(jù)。這是我們?cè)陂_(kāi)發(fā)移動(dòng)應(yīng)用時(shí)始終要牢記的:移動(dòng)設(shè)備在整個(gè)應(yīng)用系統(tǒng)中的角色是什么?
(2)查詢優(yōu)化
查詢是衡量數(shù)據(jù)庫(kù)性能的重要指標(biāo)之一。在查詢方面可優(yōu)化的有很多,例如建立索引、限制返回記錄數(shù)和where條件子句等。
使用索引,能夠提高查詢的性能。具體哪些字段需要?jiǎng)?chuàng)建索引,這很關(guān)鍵。只有在表連接或where條件子句中使用字段時(shí),才能提高查詢性能。在INTEGER PRIMARY KEY字段上,一般不用建索引。如果表中的數(shù)據(jù)很少,則建索引的效果不大。
由于移動(dòng)設(shè)備屏幕相對(duì)來(lái)說(shuō)比較小,屏幕上能顯示的數(shù)據(jù)不多,如果一次查詢出的記錄數(shù)超過(guò)屏幕能顯示的行數(shù),這就沒(méi)有必要了,因?yàn)檫@樣反而會(huì)占用更多的內(nèi)存,耗費(fèi)寶貴的CPU時(shí)間。因此,我們需要為查詢添加返回記錄數(shù)的限制。下面的語(yǔ)句是SQLite支持的寫(xiě)法:
SELECT * FROM Note Limit 10 Offset 5;
以上語(yǔ)句表示從Note表查詢數(shù)據(jù)出來(lái),其中10表示查詢的最大記錄數(shù)不超過(guò)10個(gè),5表示偏移量,即跳過(guò)5行取10個(gè)。
在where條件子句的優(yōu)化方面,就有更多優(yōu)化方式了。比如,盡量不要使用LIKE模糊匹配查詢,如果可能,則使用=查詢;盡量不要使用IN語(yǔ)句,可以使用=和or替代。此外,在多個(gè)條件中,要把非文本的條件放在前面,文本條件放在后面,示例代碼如下:
(salary > 5000000) AND (lastName LIKE 'Guan') 優(yōu)于 (lastName LIKE 'Guan') AND (salary > 5000000)
這是因?yàn)榉俏谋镜臈l件判斷比較快,如果不滿足,就不用再計(jì)算后面的條件表達(dá)式了。
(3)插入(或刪除)優(yōu)化
索引可以提供查詢性能,但是對(duì)于插入和刪除是有負(fù)面影響的。索引就像是書(shū)中的目錄,插入和刪除數(shù)據(jù)必然造成索引重排,所以創(chuàng)建索引要慎重。
在SQLite中,有一些PRAGMA指令可以改變數(shù)據(jù)庫(kù)的行為。PRAGMA synchronous指令用于設(shè)置數(shù)據(jù)同步操作。同步是指在插入數(shù)據(jù)時(shí),將數(shù)據(jù)同時(shí)保存到存儲(chǔ)介質(zhì)中。如果PRAGMA synchronous = OFF,則表示關(guān)閉了數(shù)據(jù)同步,不等待數(shù)據(jù)保存到存儲(chǔ)介質(zhì)就可繼續(xù)執(zhí)行插入操作,這在大量數(shù)據(jù)插入時(shí)可以大大提高速度。在Objective-C中,可以調(diào)用sqlite3_exec函數(shù)設(shè)置數(shù)據(jù)是否同步,相關(guān)語(yǔ)句如下:
sqlite3_open(DATABASE, &db)
sqlite3_exec(db, "PRAGMA synchronous = OFF", nil, nil, nil)
插入完成后,也可以重新設(shè)置PRAGMA synchronous = NORMAL或PRAGMA synchronous = FULL。
4.3 使用 Core Data
Core Data是面向?qū)ο蟮腛RM技術(shù),蘋(píng)果公司推薦使用。它提供了緩沖、延遲加載等技術(shù),其性能比較好,但有時(shí)候我們會(huì)發(fā)現(xiàn)它的性能要比SQLite差,這主要與存儲(chǔ)類型的設(shè)置有關(guān)。Core Data的存儲(chǔ)類型有NSSQLiteStoreType、NSBinaryStoreType和NSInMemoryStoreType,我們主要采用NSSQLiteStoreType類型,這樣底層存儲(chǔ)就采用了SQLite數(shù)據(jù)庫(kù),SQLite數(shù)據(jù)庫(kù)的優(yōu)點(diǎn)也能發(fā)揮出來(lái)。
使用Core Data時(shí),還要考慮查詢優(yōu)化問(wèn)題。它的查詢是通過(guò)NSFetchRequest執(zhí)行Predicate定義的邏輯查詢條件實(shí)現(xiàn)的,在優(yōu)化規(guī)則上與SQLite的where條件子句是一樣的。此外,如果要查詢返回記錄數(shù)的限制,可以使用如下語(yǔ)句:
這兩條語(yǔ)句相當(dāng)于SELECT * FROM Note Limit 10 Offset 5;。
此外,還可以設(shè)置pragma指令,相關(guān)語(yǔ)句如下:
在上述代碼中,我們首先把這些pragma指令放置于NSMutableDictionary可變字典中,然后以NSSQLitePragmasOption為鍵再把指令設(shè)置到可變字典中。NSPersistentStore對(duì)象的addPersistentStoreWithType:configuration:URL:
options:error:方法的options參數(shù)用于接收設(shè)置的NSDictionary對(duì)象。
為了方便分析Core Data的執(zhí)行情況,我們可以使用Instruments工具中的Core Data跟蹤模板,如圖14所示。
圖14 選擇Core Data跟蹤模板
進(jìn)入Core Data跟蹤模板后,如圖15所示,可以看到其內(nèi)部有3個(gè)跟蹤項(xiàng)目:Core Data Fetches、Core DataCache和Core Data Saves。
圖15 Core Data跟蹤模板
當(dāng)我們執(zhí)行查詢、插入和刪除操作時(shí),在Core Data Fetches和Core Data Saves的跟蹤項(xiàng)目右邊會(huì)產(chǎn)生很多線。
其中,①部分的線段為Fetch count(查詢的記錄數(shù)據(jù));②部分的線段為Fetch duration(執(zhí)行查詢的持續(xù)時(shí)間),將虛線拉到上面可以看到這些內(nèi)容的具體數(shù)值;如果有數(shù)據(jù)要保存,③部分產(chǎn)生的線段為Save duration(保存所持續(xù)的時(shí)間);④部分是更具體的信息,F(xiàn)etch entity列是查詢的實(shí)體類(Note),F(xiàn)etch count列是查詢的記錄數(shù),F(xiàn)etch duration列是查詢的執(zhí)行時(shí)間。
了解更多相關(guān)資訊,關(guān)注南昌網(wǎng)絡(luò)公司--百恒網(wǎng)絡(luò)官方網(wǎng)站。百恒網(wǎng)絡(luò)是一家專業(yè)從事南昌網(wǎng)站建設(shè)、微信開(kāi)發(fā)、APP開(kāi)發(fā)、網(wǎng)絡(luò)營(yíng)銷等服務(wù)的南昌網(wǎng)絡(luò)公司,技術(shù)過(guò)硬,經(jīng)驗(yàn)豐富。如有任何網(wǎng)站方面的問(wèn)題,百恒網(wǎng)絡(luò)隨時(shí)歡迎大家來(lái)電咨詢,我們專業(yè)為您解答!