午夜国产狂喷潮在线观看|国产AⅤ精品一区二区久久|中文字幕AV中文字幕|国产看片高清在线

    如何避免iphone應(yīng)用中內(nèi)存泄露
    來源:易賢網(wǎng) 閱讀:914 次 日期:2015-02-03 14:20:14
    溫馨提示:易賢網(wǎng)小編為您整理了“如何避免iphone應(yīng)用中內(nèi)存泄露”,方便廣大網(wǎng)友查閱!

    創(chuàng)建對象時(shí),所有權(quán)通過alloc、new、或者copy的方式建立,之后通過調(diào)用retain或者通過cocoa函數(shù)來分配和復(fù)制對象的所有權(quán)。 內(nèi)存釋放有兩種方式,一種方法是明確地請求釋放對象的所有權(quán),另一種方法則是使用自動釋放池(auto-release pool)。

    所有權(quán)的背后是一個(gè)和引用有關(guān)的運(yùn)算系統(tǒng),iphone sdk的大多數(shù)對象使用這個(gè)系統(tǒng),彼此之間建立著很強(qiáng)的引用和參照。

    當(dāng)你創(chuàng)建一個(gè)對象時(shí),引用值為1,調(diào)用一次retain則對象的引用值加1,調(diào)用一次release則對象的引用值減1,當(dāng)引用值為0時(shí),對象的所有權(quán)分配將被取消。使用自動釋放池意味著對象的所有權(quán)將在一段延后的時(shí)間內(nèi)被自動取消。

    對象之間也可以建立弱的引用參照,此時(shí)意味著,引用值不會被保留,對象的分配需要手動取消。

    什么時(shí)候使用retain?

    什么時(shí)候你想阻止對象在使用前就被釋放?

    每當(dāng)使用copy、alloc、retain、或者cocoa函數(shù)來創(chuàng)建和復(fù)制所有權(quán),你都需要相應(yīng)的release或者auto-release。

    開發(fā)者應(yīng)該從所有權(quán)的角度來考慮對象,而不必?fù)?dān)心引用值。只要你有相應(yīng)的retain和release方法,就能夠?qū)σ弥颠M(jìn)行+1和-1操作。

    注意:你或許想使用[object retaincount],但它可能因?yàn)閟dk的底層代碼而發(fā)生返回值出錯(cuò)的情況。在內(nèi)存管理時(shí)不推薦這種方式。

    自動釋放

    將對象設(shè)置為自動釋放意味著不需要明確地請求釋放,因?yàn)楫?dāng)自動釋放池清空時(shí)它們將被自動釋放。iphone在主線程上運(yùn)行自動釋放池,能夠在事件循環(huán)結(jié)束后釋放對象。當(dāng)你創(chuàng)建你自己的線程時(shí),你需要創(chuàng)建自己的自動釋放池。

    iphone上有便利的構(gòu)造函數(shù),用這種方法創(chuàng)建的對象會設(shè)置為自動釋放。

    例子:

    nsstring* str0 = @hello;

    nsstring* str1 = [nsstring stringwithstring:@world];

    nsstring* str2 = str1;

    一個(gè)已分配的對象可以用如下的方法設(shè)置為自動釋放:

    nsstring* str = [[nsstring alloc] initwithstring:@the flash?];

    [str autorelease];

    或者用下面的方法:

    nsstring* str = [[[nsstring alloc] initwithstring:@batman!] autorelease];

    當(dāng)指針出界,或者當(dāng)自動釋放池清空時(shí),自動釋放對象上的所有權(quán)將被取消。

    在一個(gè)事件循環(huán)結(jié)束時(shí),自動釋放池內(nèi)的構(gòu)件通常會被清空。但是當(dāng)你的循環(huán)每次迭代都分配大量內(nèi)存時(shí),你或許希望這不要發(fā)生。這種情況下,你可以在循 環(huán)內(nèi)創(chuàng)建自動釋放池。自動釋放池可以嵌套,所以內(nèi)部池清空時(shí),其中分配的對象將被釋放。在下面的例子中,每次迭代后將釋放對象。

    for (int i = 0; i < 10; ++i)

    {

    nsautoreleasepool *pool = [[nsautoreleasepool alloc] init];

    nsstring* str = [nsstring stringwithstring:@hello world];

    [self processmessage: str];

    [pool drain];

    }

    注意:在編寫的時(shí)候iphone不支持垃圾回收,所以drain和release的功能相同。當(dāng)你想為程序設(shè)置osx的端口時(shí)通常會使用drain,除非后來在iphone中添加了垃圾回收機(jī)制。drain能夠擊發(fā)垃圾回收器釋放內(nèi)存。

    返回一個(gè)對象的指針

    開發(fā)者在遵循所有權(quán)規(guī)則時(shí)需要清楚哪些函數(shù)擁有對象的所有權(quán)。下面是返回一個(gè)對象的指針并釋放的例子。

    錯(cuò)誤的方法:

    - (nsmutablestring*) getoutput

    {

    nsmutablestring* output = [[nsmutablestring alloc] initwithstring:@output];

    return output;

    }

    - (void) test

    {

    nsmutablestring* obj = [self getoutput];

    nslog(@count: %d, [obj retaincount]);

    [obj release];

    }

    在這個(gè)例子中,output 的所有者是 getoutput,讓 test 釋放 obj 違反了coccoa內(nèi)存管理指南中的規(guī)則,盡管它不會泄露內(nèi)存但是這樣做不好,因?yàn)閠est 不應(yīng)該釋放并非它所擁有的對象。

    正確的方法:

    - (nsmutablestring*) getoutput

    {

    nsmutablestring* output = [[nsmutablestring alloc] initwithstring:@output];

    return [output autorelease];

    }

    - (void) test

    {

    nsmutablestring* obj = [self getoutput];

    nslog(@count: %d, [obj retaincount]);

    }

    在第二個(gè)例子中,output 被設(shè)置為當(dāng) getoutput 返回時(shí)自動釋放。output的引用值減少,getobject 釋放 output 的所有權(quán)。test 函數(shù)現(xiàn)在可以自由的 retain 和 release 對象,請確保它不會泄露內(nèi)存。

    例子中 obj 被設(shè)置為自動釋放,所以 test 函數(shù)沒有它的所有權(quán),但是如果它需要在其他地方存儲對象會怎樣?

    此時(shí)對象需要有一個(gè)新的所有者來保留。

    setters

    setter函數(shù)必須保留它所存儲的對象,也就是聲明所有權(quán)。如果我們想要創(chuàng)建一個(gè) setter 函數(shù),我們需要在分配一個(gè)新的指向成員變量的指針之前做兩件事情。

    在函數(shù)里:

    - (void) setname:(nsstring*)newname

    首先我們要減少成員變量的引用值:

    [name release];

    這將允許當(dāng)引用值為0時(shí) name 對象被釋放,但是它也允許對象的其他所有者繼續(xù)使用對象。

    然后我們增加新的 nsstring 對象的引用值:

    [newname retain];

    所以當(dāng) setname 結(jié)束時(shí), newname 不會被取消分配。 newname 現(xiàn)在指向的對象和 name 指向的對象不同,兩者有不同的引用值。

    現(xiàn)在我們設(shè)置 name 指向 newname 對象:

    name = newname;

    但是如果 name 和 newname 是同一個(gè)對象時(shí)怎么辦?我們不能在它被釋放后保留它,并再次釋放。

    在釋放存儲的對象前保留新的對象:

    [newname retain];

    [name release];

    name = newname;

    現(xiàn)在兩個(gè)對象是相同的,先增加它的引用值,然后再減少,從而使得賦值前引用值不變。

    另一種做法是使用 objective-c:

    聲明如下:

    @property(nonatomic, retain) nsstring *name;

    1. nonatomic 表示沒有對同一時(shí)間獲取數(shù)據(jù)的多個(gè)線程進(jìn)行組塊兒。atomic 為一個(gè)單一的線程鎖定數(shù)據(jù),但因?yàn)?atomic 的方式比較緩慢,所以不是必須的情況一般不使用。

    2. retain 表示我們想要保留 newname 對象。

    我們可以使用 copy 代替 retain:

    @property(nonatomic, copy) nsstring *name;

    這和下面的函數(shù)一樣:

    - (void) setname:(nsstring*)newname

    {

    nsstring* copiedname = [newname copy];

    [name release];

    name = copiedname;

    [name retain];

    [copiedname release];

    }

    newname 在這里被復(fù)制到 copiedname,現(xiàn)在 copiedname 擁有串的一個(gè)副本。name 被釋放,而 copiedname 被賦給 name。之后 name 保留這個(gè)串,從而使得 copiedname 和 name 同時(shí)擁有它。最后 copiedname 釋放這個(gè)對象,name 成為這個(gè)串的唯一所有者。

    如果我們有如下的函數(shù),像這樣的 setters 將被輸入用來保留成員對象:

    - (void) test

    {

    nsautoreleasepool *pool = [[nsautoreleasepool alloc] init];

    // do something...

    name = [self getoutput];

    // do something else...

    nslog(@client name before drain: %@, name);

    [pool drain];

    nslog(@client name after drain: %@, name);

    }

    name 在調(diào)用至 drain 后是未定義的,因?yàn)楫?dāng)池被釋放時(shí),name 也將被釋放。

    如果我們用如下的部分替代賦值:

    [self setname:[self getoutput]];

    然后 name 將被這個(gè)類所有,在使用時(shí)保留直到調(diào)用 release

    那么我們何時(shí)釋放對象?

    由于 name 是成員變量,釋放它的最安全的辦法是對它所屬的類使用 dealloc 函數(shù)。

    - (void)dealloc

    {

    [name release];

    [super dealloc];

    }

    注意:雖然并不總是調(diào)用 dealloc,依靠 dealloc 來釋放對象可能是危險(xiǎn),可能會觸發(fā)一些想不到的事情。在出口處,iphone os 可能在調(diào)用 dealloc 前清空全部應(yīng)用程序的內(nèi)存。

    當(dāng)用 setter 給對象賦值時(shí),請小心下面的語句:

    [self setname:[[nsstring alloc] init]];

    name 的設(shè)置是正確的但 alloc 沒有相應(yīng)的釋放,下面的方式要好一些:

    nsstring* s = [[nsstring alloc] init];

    [self setname:s];

    [s release];

    或者使用自動釋放:

    [self setname:[[[nsstring alloc] init] autorelease]];

    自動釋放池

    自動釋放池釋放位于分配和 drain 函數(shù)之間的對象。

    我們在下面的函數(shù)中設(shè)置一個(gè)循環(huán),在循環(huán)中將 nsnumber 的一個(gè)副本賦給 magicnumber,另外將 magicnumber 設(shè)置為自動釋放。在這個(gè)例子中,我們希望在每次迭代時(shí)清空自動釋放池(這樣可以在賦值的數(shù)量很大時(shí)節(jié)省循環(huán)的內(nèi)存)

    - (void) test

    {

    nsstring* clientname = nil;

    nsnumber* magicnumber = nil;

    for (int i = 0; i < 10; ++i)

    {

    nsautoreleasepool *pool = [[nsautoreleasepool alloc] init];

    magicnumber = [[self getmagicnumber] copy];

    [magicnumber autorelease];

    if (i == [magicnumber intvalue])

    {

    clientname = [self getoutput];

    }

    [pool drain];

    }

    if (clientname != nil)

    {

    nslog(@client name: %@, clientname);

    }

    }

    這里存在的問題是 clientname 在本地的自動釋放池中被賦值和釋放,所以當(dāng)外部的池清空時(shí),clientname 已經(jīng)被釋放了,任何對 clientname 的進(jìn)一步使用都是沒有定義的。

    在這個(gè)例子中,我們在賦值后保留 clientname,直到結(jié)束時(shí)再釋放它:

    - (void) test

    {

    nsstring* clientname = nil;

    nsnumber* magicnumber = nil;

    for (int i = 0; i < 10; ++i)

    {

    nsautoreleasepool *pool = [[nsautoreleasepool alloc] init];

    magicnumber = [[self getmagicnumber] copy];

    [magicnumber autorelease];

    if (i == [magicnumber intvalue])

    {

    clientname = [self getoutput];

    [clientname retain];

    }

    [pool drain];

    }

    if (clientname != nil)

    {

    nslog(@client name: %@, clientname);

    [clientname release];

    }

    }

    我們在調(diào)用 retain 函數(shù)和 release 函數(shù)的期間獲得 clientname 的所有權(quán)。通過添加一對 retain 和 release 的調(diào)用,我們就確保 clientname 在明確調(diào)用釋放前不會被自動釋放。

    集合

    當(dāng)一個(gè)對象被添加進(jìn)集合時(shí),它就被集合所擁有。在這個(gè)例子中我們分配一個(gè)串,它現(xiàn)在有了所有者;

    nsstring* str = [[nsstring alloc] initwithstring:@bruce wayne];

    然后我們將它添加進(jìn)數(shù)組,現(xiàn)在它有兩個(gè)所有者:

    [array addobject: str];

    我們可以安全的釋放這個(gè)串,使其僅被數(shù)組所有:

    [str release];

    當(dāng)一個(gè)集合被釋放時(shí),其中的所有對象都將被釋放。

    nsmutablearray* array = [[nsmutablearray alloc] init];

    nsstring* str = [[nsstring alloc] initwithstring:@bruce wayne];

    [array addobject: str];

    [array release];

    在上面的例子中,我們分配了一個(gè)數(shù)組和一個(gè)串,然后將串添加到數(shù)組中并釋放數(shù)組。這使得串僅擁有一個(gè)所有者,并且在我們調(diào)用 [str release] 前它不會被釋放。

    用線程傳遞指針

    在這個(gè)函數(shù)中,我們從串的 input 傳遞到函數(shù) dosomething,然后釋放 input

    - (void) test

    {

    nsmutablestring* input = [[nsmutablestring alloc] initwithstring:@batman!];

    [nsthread detachnewthreadselector:@selector(dosomething:) totarget:self withobject:input];

    [input release];

    }

    detatchnewthreadselector 增加 input 對象的引用值并在線程結(jié)束時(shí)釋放它。這就是為什么我們能夠在線程剛開始的時(shí)候就釋放 input,而無論函數(shù) dosomething 何時(shí)開始或結(jié)束。

    - (void) dosomething:(nsstring*)str

    {

    [self performselectoronmainthread:@selector(finishsomething:) withobject:str waituntildone:false];

    }

    performseclectoronmainthread 也會保留傳遞的對象,直到 selector 結(jié)束。

    自動釋放池是特殊的線程,所以如果我們在一個(gè)新的線程上創(chuàng)建自動釋放的對象,我們需要創(chuàng)建一個(gè)自動釋放池來釋放它們。

    [nsthread detachnewthreadselector:@selector(process) totarget:self withobject:nil];

    這里在另一個(gè)線程上調(diào)用函數(shù) process

    - (void) process

    {

    nsautoreleasepool *pool = [[nsautoreleasepool alloc] init];

    nsmutablestring* output = [[[nsmutablestring alloc] initwithstring:@batman!] autorelease];

    nslog(@output: %@, output);

    [self performselectoronmainthread:@selector(finishprocess) withobject:nil waituntildone:false];

    [pool drain];

    }

    對象 output 被分配并且在自動釋放池中設(shè)置了自動釋放,它將在函數(shù)結(jié)束前被釋放。

    - (void) finishprocess

    {

    nsmutablestring* output = [[[nsmutablestring alloc] initwithstring:@superman?] autorelease];

    nslog(@output: %@, output);

    }

    系統(tǒng)會為主線程自動創(chuàng)建一個(gè)自動釋放池,所以在 finishprocess 中,我們不需要為主線程上運(yùn)行的函數(shù)創(chuàng)建自動釋放池。

    更多信息請查看IT技術(shù)專欄

    更多信息請查看技術(shù)文章
    易賢網(wǎng)手機(jī)網(wǎng)站地址:如何避免iphone應(yīng)用中內(nèi)存泄露
    由于各方面情況的不斷調(diào)整與變化,易賢網(wǎng)提供的所有考試信息和咨詢回復(fù)僅供參考,敬請考生以權(quán)威部門公布的正式信息和咨詢?yōu)闇?zhǔn)!

    2025國考·省考課程試聽報(bào)名

    • 報(bào)班類型
    • 姓名
    • 手機(jī)號
    • 驗(yàn)證碼
    關(guān)于我們 | 聯(lián)系我們 | 人才招聘 | 網(wǎng)站聲明 | 網(wǎng)站幫助 | 非正式的簡要咨詢 | 簡要咨詢須知 | 新媒體/短視頻平臺 | 手機(jī)站點(diǎn) | 投訴建議
    工業(yè)和信息化部備案號:滇ICP備2023014141號-1 云南省教育廳備案號:云教ICP備0901021 滇公網(wǎng)安備53010202001879號 人力資源服務(wù)許可證:(云)人服證字(2023)第0102001523號
    云南網(wǎng)警備案專用圖標(biāo)
    聯(lián)系電話:0871-65099533/13759567129 獲取招聘考試信息及咨詢關(guān)注公眾號:hfpxwx
    咨詢QQ:1093837350(9:00—18:00)版權(quán)所有:易賢網(wǎng)
    云南網(wǎng)警報(bào)警專用圖標(biāo)