ios 內(nèi)存優(yōu)化和調(diào)試技巧
來(lái)源:易賢網(wǎng) 閱讀:843 次 日期:2014-11-18 11:14:40
溫馨提示:易賢網(wǎng)小編為您整理了“ios 內(nèi)存優(yōu)化和調(diào)試技巧”,方便廣大網(wǎng)友查閱!

基礎(chǔ)部分

1: 圖片內(nèi)存大小小結(jié)

a: 圖片:是占用內(nèi)存的大戶(hù),尤其是手機(jī)游戲圖片資源眾多。對(duì)圖片資源在內(nèi)存中占用量的計(jì)算成為j2me游戲開(kāi)發(fā)者的經(jīng)常性工作,cocomo來(lái)解釋一下如何計(jì)算圖片在內(nèi)存中的占用量:內(nèi)存占用量=寬*高*像素字節(jié)數(shù),其中像素字節(jié)數(shù)因機(jī)型而異。

例如一張64*64的圖片在7210上的內(nèi)存占用量=64*64*1.5=6144(字節(jié))=6k、在s60上的內(nèi)存占用量=64*64*2=8192 (字節(jié))=8k。像素字節(jié)數(shù)因機(jī)型而異,例如 7210是4096色機(jī)型,也就是說(shuō)用12位來(lái)表示一個(gè)像素,所以乘上1.5,而s60是65536色的機(jī)型,用16位來(lái)表示一個(gè)像素,所以乘上2。

b:xcode中使用instruments 查看圖片內(nèi)存的問(wèn)題

如果使用的是模擬器那么默認(rèn)是小屏幕的,所以最大圖片是1024 *1024 * 4 = 4 m (1024 是圖片的寬高, 4表示的是圖片的存儲(chǔ)類(lèi)型為4字節(jié)的。也就是 rgba8888)

如果你加載了圖片那么就是使用了4m的內(nèi)存。如果你需要渲染那么還需要4m的內(nèi)存。

加載一般都是 **load (nsstring *)filename ,

渲染一般都是 node addchild (node)

2: 引用計(jì)數(shù)問(wèn)題

引用計(jì)數(shù)增加的情況 : a: alloc 對(duì)象會(huì)使得對(duì)象引用數(shù) +1

b:調(diào)用retain (具體細(xì)說(shuō)一些實(shí)例如下)

->比如你是cocos2d用戶(hù)的會(huì)看到 addchild 會(huì)使子節(jié)點(diǎn)的引用計(jì)數(shù)+1

->ccarray 的addobject 也會(huì)使元素的引用計(jì)數(shù)+1

總結(jié)一下就是: 凡是添加到結(jié)合中的元素或者子節(jié)點(diǎn)不需要再去retain ,只需要在建立的時(shí)候調(diào)用release

減少的情況 : 調(diào)用release 使引用計(jì)數(shù) -1(具體細(xì)說(shuō)一些實(shí)例如下)

-> 集合調(diào)用remove/removechildbytag 等等變形的

-> 創(chuàng)建的時(shí)候調(diào)用autorelease 。注意:如果你的對(duì)象是局部對(duì)象,而且創(chuàng)建的時(shí)候使用的是autorelease,

那么在離開(kāi)方法的時(shí)候如果你沒(méi)有retain 那么這個(gè)對(duì)象將被dealloc(引用計(jì)數(shù)-1了)

官網(wǎng)的介紹:

you own any object you create by allocating memory for it or copying it.

related methods:alloc,allocwithzone:,copy,copywithzone:,mutablecopy,mutablecopywithzone:

if you are not the creator of an object, but want to ensure it stays in memory for you to use, you can express an ownership interest in it.

related method:retain

if you own an object, either by creating it or expressing an ownership interest, you are responsible for releasing it when you no longer need it.

related methods:release,autorelease

conversely, if you are not the creator of an object and have not expressed an ownership interest, you mustnotrelease it.

3 :參考文檔

一,ios與圖片內(nèi)存

在ios上,圖片會(huì)被自動(dòng)縮放到2的n次方大小。比如一張1024*1025的圖片,占用的內(nèi)存與一張1024*2048的圖片是一致的。圖片占用內(nèi)存大小的計(jì)算的公式是;長(zhǎng)*寬*4。這樣一張512*512占用的內(nèi)存就是 512*512*4 = 1m。其他尺寸以此類(lèi)推。(ps:ios上支持的最大尺寸為2048*2048)。

,cocos2d-x的圖片緩存

cocos2d-x 在構(gòu)造一個(gè)精靈的時(shí)候會(huì)使用spritewithfile或者spritewithspriteframename等無(wú)論用哪種方式,cocos2d-x都會(huì)將這張圖片加載到緩存中。如果是第一次加載這個(gè)圖片,那就會(huì)先將這張圖片加載到緩存,然后從緩存讀取。如果緩存中已經(jīng)存在,則直接從緩存中提取,免除了加載過(guò)程。

圖片的緩存主要由以下兩個(gè)類(lèi)來(lái)處理:ccspriteframecache, cctexturecache

ccspriteframecache加載的是一張拼接過(guò)的大圖,每一個(gè)小圖只是大圖中的一個(gè)區(qū)域,這些區(qū)域信息都在plist文件中保存。用的時(shí)候只需要根據(jù)小圖的名稱(chēng)就可以加載到這個(gè)區(qū)域。

cctexturecache 是普通的圖片緩存,我們所有直接加載的圖片都會(huì)默認(rèn)放到這個(gè)緩存中,以提高調(diào)用效率。

因此,每次加載一張圖片,或者通過(guò)plist加載一張拼接圖時(shí),都會(huì)將整張圖片加載到內(nèi)存中。如果不去釋放,那就會(huì)一直占用著。

三,渲染內(nèi)存。

不要以為,計(jì)算內(nèi)存時(shí),只計(jì)算加載到緩存中的內(nèi)存就可以了。以一張1024*1024的圖片為例。

ccsprite *psprite = ccsprite::spritewithfile(a.png);

調(diào)用上邊這行代碼以后,可以在leaks工具中看到,增加了大約4m的內(nèi)存。然后接著調(diào)用

addchild(psprite);

這時(shí),內(nèi)存又增加了4m。也就是,一張圖片,如果需要渲染的話,那它所占用的內(nèi)存將要x2。

再看看通過(guò)plist加載的圖片,比如這張大圖尺寸為2048*2048。想要加載其中的一張32*32的小圖片

ccspriteframecache::sharedspriteframecache()->addspriteframeswithfile(b.plist);

此時(shí)內(nèi)存增加16m (汗)

ccsprite *pspriteframe = ccsprite::spritewithspriteframename(b1.png);

b.png 大小為32*32,想著也就是增加一點(diǎn)點(diǎn)內(nèi)存,可實(shí)際情況是增加16m內(nèi)存。也就是只要渲染了其中的一部分,那么整張圖片都要一起被加載。

但是情況不是那么的糟糕,這些已經(jīng)渲染的圖片,如果再次加載的話,內(nèi)存是不會(huì)再繼續(xù)升高的,比如又增加了100個(gè)b.plist的另一個(gè)區(qū)域,圖片內(nèi)存還是共增加16+16 = 32m,而不會(huì)繼續(xù)上升。

四,緩存釋放

如果游戲有很多場(chǎng)景,在切換場(chǎng)景的時(shí)候可以把前一個(gè)場(chǎng)景的內(nèi)存全部釋放,防止總內(nèi)存過(guò)高.

cctexturecache::sharedtexturecache()->removealltextures();釋放到目前為止所有加載的圖片

cctexturecache::sharedtexturecache()->removeunusedtextures();將引用計(jì)數(shù)為1的圖片釋放掉cctexturecache::sharedtexturecache()->removetexture();單獨(dú)釋放某個(gè)圖片

ccspriteframecache與 cctexturecache 釋放的方法差不多。

值得注意的是釋放的時(shí)機(jī),一般在切換場(chǎng)景的時(shí)候釋放資源,如果從a場(chǎng)景切換到b場(chǎng)景,調(diào)用的函數(shù)順序?yàn)閎::init()---->a::exit()---->b::onenter()可如果使用了切換效果,比如ctransitionjumpzoom::transitionwithduration這樣的函數(shù),則函數(shù)的調(diào)用順序變?yōu)閎::init()---->b::onenter()---->a::exit()而且第二種方式會(huì)有一瞬間將兩個(gè)場(chǎng)景的資源疊加在一起,如果不采取過(guò)度,很可能會(huì)因?yàn)閮?nèi)存吃緊而崩潰。

有時(shí)強(qiáng)制釋放全部資源時(shí),會(huì)使某個(gè)正在執(zhí)行的動(dòng)畫(huà)失去引用而彈出異常,可以調(diào)用ccactionmanager::sharedmanager()->removeallactions();來(lái)解決。

五,內(nèi)存優(yōu)化

優(yōu)化的心得就是盡量去拼接圖片,使圖片邊長(zhǎng)盡可能的保持2的n次方并且裝的很滿(mǎn)。但要注意,有邏輯關(guān)系的圖片盡量打包在一張大圖里,另外一點(diǎn)就是打包的時(shí)候要考慮到層的分布。因?yàn)闉榱虽秩拘士赡軙?huì)用到ccspritebatchnode;同一個(gè)batchnode里的圖片都是位于一個(gè)層級(jí)的,因此必須根據(jù)各個(gè)圖片的層級(jí)關(guān)系,打包到不同的plist里。有時(shí)內(nèi)存和效率不可以兼得,只能盡量平衡了。

六,其他

最后附一個(gè)各代ios設(shè)備的內(nèi)存限制情況

設(shè)備 建議內(nèi)存 最大內(nèi)存

ipad2/iphone4s/iphone4 170-180mb 512mb

ipad/ipod touch3,4/iphone3gs 40-80mb 256mb

ipod touch1,2/iphone3g/iphone1 25mb 128mb

上述建議內(nèi)存只是一些人自己測(cè)試的結(jié)果,可用的ram不大于最大內(nèi)存的一半,如果程序超過(guò)最大內(nèi)存的一半,則可能會(huì)掛掉。

另外在leaks里查看模擬器中和真機(jī)總的內(nèi)存,會(huì)有較大出入。在模擬器中的結(jié)果與實(shí)際更接近一些。

七, 泄漏的情況

我所碰到的主要內(nèi)存泄露的方式:

1、最常見(jiàn)的就是,申請(qǐng)了引用,然后最后忘記釋放。具體么就是,使用oc的 alloc, retain, copy, new, c的malloc, realloc, c++的new等,然后沒(méi)有對(duì)應(yīng)的release, free, delete。這是單向泄露。

2、retain cycle,對(duì)于oc這種使用計(jì)數(shù)的方式,可能會(huì)存在retain cycle。兩個(gè)條件,一、就是a中retain了b,b又retain了a,各自給對(duì)方計(jì)數(shù)增加,這個(gè)環(huán)可以變?yōu)楹芏鄬?,就是a->b, b->c, c->d, .... z->a,當(dāng)然假如中間層越多,檢測(cè)難度就越大。二、計(jì)數(shù)減少的操作是在dealloc中,而dealloc被調(diào)用則需要計(jì)數(shù)為0。 這兩個(gè)條件相加,導(dǎo)致計(jì)數(shù)鎖定,內(nèi)存泄露。

實(shí)戰(zhàn)演練

如何查找內(nèi)存泄漏 ?

一:對(duì)工具的使用來(lái)查找

1、首先使用分析編譯,analyze build,查看歸類(lèi)當(dāng)中的memory警告。

這個(gè)一般能發(fā)現(xiàn)局部變量中忘記release,或者被中途打斷release的。

2、然后就是直接使用instruments中的leak監(jiān)測(cè)。

申請(qǐng)了內(nèi)存,然后已經(jīng)沒(méi)有指向這塊內(nèi)存的指針存在,可以認(rèn)為是leak了。這個(gè)檢測(cè)一般是檢測(cè)這個(gè)狀態(tài)。

3、通過(guò)instruments中allocation的mark heap。

進(jìn)行不斷的重復(fù)操作,在每次場(chǎng)景結(jié)束后,標(biāo)記內(nèi)存。假如操作場(chǎng)景沒(méi)有泄露,內(nèi)存增加應(yīng)該是0。這個(gè)檢測(cè)是檢測(cè)標(biāo)記點(diǎn)之間有哪些對(duì)象增加。另外,需要多mark幾次才會(huì)準(zhǔn)確,不要mark兩次看到有內(nèi)存增加就去找問(wèn)題。

instruments中都是可以看到其中存在什么對(duì)象,調(diào)用歷史,調(diào)用堆棧。這時(shí)候大致確定在那個(gè)類(lèi)當(dāng)中的那個(gè)對(duì)象泄露了。

二 ,重載法。

雖然知道了哪個(gè)類(lèi)泄露了,但是有時(shí)候并不知道具體是那邊的計(jì)數(shù)出現(xiàn)問(wèn)題。我自己的方法是,假如是自己編寫(xiě)的類(lèi),那就重載retain和release方法,然后加斷點(diǎn)。以此來(lái)監(jiān)測(cè)是什么地方retain了這個(gè)對(duì)象,卻沒(méi)有對(duì)應(yīng)釋放。

如何修改內(nèi)存泄漏呢 ?

1、缺啥補(bǔ)啥。缺release的,就補(bǔ)release,缺free的就加個(gè)free。

2、合理使用autorelease。對(duì)于返回給上層使用的;或者alloc對(duì)象到release中間有return等打斷操作的。建議使用autorelease。

3、合理使用assign。retain cycle,本質(zhì)就是多余的雙向retain。打個(gè)比方就是應(yīng)該確定哪個(gè)對(duì)象是根,哪一個(gè)是枝葉,枝葉不用去管理根,只需要知道根在那邊就可以了。所以把那些純粹是定位用的變量,屬性都改成assign方式,例如delegate。

ps:

假如對(duì)于instruments的使用不是很清楚,可以看這個(gè)視頻

游戲中我遇到的一個(gè)非常難查的泄漏這里貢獻(xiàn)出來(lái) :

對(duì)于cocos2d的用戶(hù)如果使用了ccmenu ,而且也重寫(xiě)了ccscene中的onexsit 函數(shù)來(lái)檢測(cè)離開(kāi)場(chǎng)景的時(shí)候的一些變化。但是忘了去調(diào)用super onexsit

這時(shí)候ccmenu自己注冊(cè)了一個(gè)事件delegate 就無(wú)法釋放導(dǎo)致ccmenu一直無(wú)法釋放。當(dāng)加載到了其他場(chǎng)景的時(shí)候事件總會(huì)不對(duì)。就是因?yàn)檫@個(gè)導(dǎo)致的

解決辦法自然是調(diào)用super onexsit 。因?yàn)樵谶@里他釋放了delegate

更多信息請(qǐng)查看IT技術(shù)專(zhuān)欄

更多信息請(qǐng)查看技術(shù)文章
易賢網(wǎng)手機(jī)網(wǎng)站地址:ios 內(nèi)存優(yōu)化和調(diào)試技巧
由于各方面情況的不斷調(diào)整與變化,易賢網(wǎng)提供的所有考試信息和咨詢(xún)回復(fù)僅供參考,敬請(qǐng)考生以權(quán)威部門(mén)公布的正式信息和咨詢(xún)?yōu)闇?zhǔn)!
關(guān)于我們 | 聯(lián)系我們 | 人才招聘 | 網(wǎng)站聲明 | 網(wǎng)站幫助 | 非正式的簡(jiǎn)要咨詢(xún) | 簡(jiǎn)要咨詢(xún)須知 | 加入群交流 | 手機(jī)站點(diǎn) | 投訴建議
工業(yè)和信息化部備案號(hào):滇ICP備2023014141號(hào)-1 云南省教育廳備案號(hào):云教ICP備0901021 滇公網(wǎng)安備53010202001879號(hào) 人力資源服務(wù)許可證:(云)人服證字(2023)第0102001523號(hào)
云南網(wǎng)警備案專(zhuān)用圖標(biāo)
聯(lián)系電話:0871-65317125(9:00—18:00) 獲取招聘考試信息及咨詢(xún)關(guān)注公眾號(hào):hfpxwx
咨詢(xún)QQ:526150442(9:00—18:00)版權(quán)所有:易賢網(wǎng)
云南網(wǎng)警報(bào)警專(zhuān)用圖標(biāo)