2010-10-24 75 views
3

我是NSURLConnection的子類,並使用MGTwitterEngine作爲基礎來幫助我開始。這可能是無關緊要的。然而,我在他們的代碼中發現他們不使用他們的ivars @property@synthesize。他們已經用看起來像這樣的訪問器方法包裝了ivars:Objective-C(iPhone)ivars和內存管理

- (NSString *)identifier { 
    return [[_identifier retain] autorelease]; 
} 

我的問題是兩部分。首先retainautorelease有什麼關係?在我看來,它會取消本身,或更糟糕的是泄漏。

其次,如果我改變頭文件有:

@property (nonatomic, retain, readonly) NSString* _identifier; 

並用@synthesize indentifier = _identifier,就不會這樣做同樣的事情的訪問方法,而不必寫呢?

也許這只是兩種不同的方式來做同樣的事情。但我想確保我有正確的理解。謝謝。

回答

8

使用@synthesize實際上只會創建setter和getter方法。自動爲您生成的代碼保證使用正確的內存管理,這樣您就不必擔心。

MGTwitterEngines使用return [[ivar retain] autorelease]實際上是正確的方法。讓我們舉兩個例子。

假設一個getter被定義爲這樣的:

-(Foo)foo { 
    return foo; 
} 

然後我們執行這個代碼:1.

  • foo = bar.foo; // foo的哈日的

    1. bar = [[bar alloc] init]; //酒吧已經aretain計數保留數爲1(由酒吧擁有)。
    2. [bar release]; //酒吧和它的所有ivars都被釋放出來!
    3. [foo doSomething]; //自上一行發佈foo以後,這會崩潰。

    如果我們不是改變吸氣這樣:

    -(Foo)foo { 
        return [[foo retain] autorelease]; 
    } 
    
    1. bar = [[bar alloc] init]; //欄有1個
    2. foo = bar.foo; // FOO的保留數爲2擋計數(一體的獨資由酒吧,1由自動釋放池擁有)。
    3. [bar release]; //酒吧和它的所有ivars都被釋放出來!
    4. [foo doSomething]; //不會崩潰,因爲foo還活着並且被autorelease池所有。

    希望這解釋了爲什麼你應該總是從你所有的獲得者中正確地返回自動釋放的對象。重要的是,任何回報價值都可以在其父母的重新分配後生存下來,因爲沒有階級可以保證一旦客戶暴露於野外,客戶將如何處理這些價值。

  • +0

    這是有道理的。感謝您的詳細解釋。一個方面的問題,如果我的一個ivars是對'delegate'的引用,我不希望這樣做,而是傾向於使用具有'assign'屬性的'@ property'。也就是說,我不應該在引用類對象時調用'retain' /'autorelease',對吧?只有我的課程擁有並允許訪問者。 – 2010-10-24 17:46:57

    +0

    @Jason:對,代表不應該由內存管理,因此他們只需執行'return delegate'。如果屬性是'copy',那麼在mutator方法中執行'ivar = [arg copy]',在getter方法中執行'return [[ivar retain] autorelease]'。 – PeyloW 2010-10-25 13:04:02

    0

    一般來說,如果你正在返回一些你並不擁有第一名的東西,那麼一般會保留然後autoreleases。它只會泄漏,如果你擁有_identifier,並且保留/分配等與發送到該對象的釋放/自動釋放不匹配。

    其次,是的,你通常不必寫頭,如果你沒有做一些特殊的東西超出一般樣板看起來像。蘋果有很好的文件記錄,如果你仍然模糊不清,你可以看看這些文件。

    +1

    即使您確實「擁有」了您要返回的內容,仍然保留/ autorelease以保證該對象在調用者範圍內有效。返回屬性的對象可能不一定是那麼長。 – 2010-10-24 16:16:05

    3

    保留,然後自動釋放不正是你可能認爲它。它發送該對象的retain消息,然後將它發送一個autorelease消息。請記住,自動釋放對象將該對象添加到自動釋放池但不釋放它尚未。自動釋放池將在運行循環的當前迭代結束時向對象發送release消息。因此,隨後autorelease一個retain實際上是說,「確保這個對象總是在那裏,直到運行循環的當前迭代的結束。」如果您需要返回的值更長的時間,可以保留它。如果不是,則不執行任何操作,autorelease池將處理它。

    在這種情況下,發送的字符串retainautorelease是屬性。它已被父對象保留。所以你可能想知道爲什麼這樣保留和自動釋放東西呢?那麼,不能保證該對象在運行循環的當前迭代結束之前不會釋放_identifier。考慮下面的例子:

    - (NSString *)identifier { return _identifier; } 
    
    - (void)aMethod { 
        NSString *localId = [self identifier]; // localId refers to _identifier which is only retained by self 
        [self methodThatChangesIdentifierAndReleasesItsOldValue]; // self releases _identifier 
        NSLog(@"%@", localId); // crash, because localId (old value of _identifier) has been released 
    } 
    

    在這種情況下,返回的標識符不被保留和自動釋放。 NSLog行崩潰,因爲localId引用釋放的字符串。但是,如果我們在吸氣劑中使用了retainautorelease,則不會發生崩潰,因爲localId只有在運行循環的當前迭代結束時纔會釋放。

    據我所知,您更換identifier屬性將是一樣好。它可能不是相同的代碼,但效果應該是相同的。

    1

    蘋果在Implementing Accessor Methods解釋了這一點。您引用的方法(由Apple命名爲「Technique 1」)有助於避免錯誤,如果調用方爲標識符屬性分配新值並希望檢索值仍然可用。大部分時間不需要它,但只是返回伊娃價值可能導致難以追蹤的錯誤。