簡體   English   中英

使用alloc和init

[英]using alloc and init

我們知道必須組合alloc和init的alloc / init的完整模式。

NSObject *myObj = [[NSObject alloc] init];

1- init方法從另一個源接收對象(不是來自alloc,new,copy或類似或保留),因此根據基本的內存管理規則,它不是所有者,也不能釋放它。 但是, “分配和初始化對象/返回對象”文章說init可以釋放接收器。

當它違背基本規則時,這怎么可能呢?

2-此外,從同一篇文章中,init可以返回另一個對象或nil。 因此,在這種情況下,當我們使用alloc / init的完整模式時,我們無法釋放alloc返回的對象,但是我們只能釋放從init返回的對象,並且init釋放它從alloc而不是我們收到的對象。

但是init不是alloc,new,copy或者類似的方法,因此我們不能釋放從它返回的對象,因為它不會給我們對象的所有權。

我們如何釋放從init返回的對象,盡管這違反了基本規則?

3-或者,為了遵守同一篇文章的最后一段, 我們必須接受init方法作為特例並使用alloc / init模式作為基本規則的例外嗎?

內存管理基本規則:

  • 您只釋放或自動釋放您擁有的對象。

分配和初始化對象/返回的對象:

但是,在某些情況下,此責任可能意味着返回與接收者不同的對象。 例如,如果一個類保留了一個命名對象列表,它可能會提供一個initWithName:方法來初始化新實例。 如果每個名稱只能有一個對象,initWithName:可能會拒絕為兩個對象分配相同的名稱。 當要求為新實例分配一個已被另一個對象使用的名稱時, 它可以釋放新分配的實例並返回另一個對象 - 確保名稱的唯一性,同時提供所要求的內容,實例請求的名稱。

在少數情況下,init ...方法可能無法按照要求執行操作。 例如,initFromFile:方法可能從作為參數傳遞的文件中獲取所需的數據。 如果傳遞的文件名與實際文件不對應,則無法完成初始化。 在這種情況下, init ...方法可以釋放接收器並返回nil ,表示無法創建請求的對象。

因為init ... 方法可能返回除了新分配的接收者以外的對象,甚至返回nil所以程序使用初始化方法返回的值,而不僅僅是alloc或allocWithZone返回的值這一點很重要。 以下代碼非常危險,因為它忽略了init的返回。

 id anObject = [SomeClass alloc]; [anObject init]; [anObject someOtherMessage]; 

相反,為了安全地初始化對象,您應該在一行代碼中組合分配和初始化消息。

 id anObject = [[SomeClass alloc] init]; [anObject someOtherMessage]; 

http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/ObjectiveC/Articles/ocAllocInit.html

init方法沒有收到對象; 該對象接收init消息。 物體不具備自身; 相反,它總是知道自己(通過每條消息中隱含的self論證)。

不過,你說對象不是自己的,這是對的。 如果allocinit在一個new方法中融合,那么該方法將是它自己的( super )調用者,因此它將擁有該對象(直到它返回),因此毫無疑問正確地釋放它。 因為它們是分開的,並且init不是alloc的調用者,所以你沒有擁有該對象是正確的,所以你對這種做法提出質疑是正確的。

這是少數幾個可以讓一個對象代表另一個對象釋放對象(在本例中為自身)的情況之一。 另一種方法是不釋放它,如果你要么返回nil或拋出一個異常,那將是一個泄漏。

一般來說,只要你有一個對象保留或釋放自己,你應該感覺很臟。 在這種特定情況下 ,沒關系,因為您正在防止錯誤(泄漏)而不是創建錯誤。

2-此外,從同一篇文章中,init可以返回另一個對象或nil。 因此,在這種情況下,當我們使用alloc / init的完整模式時,我們無法釋放alloc返回的對象,但是我們只能釋放從init返回的對象,並且init釋放它從alloc而不是我們收到的對象。

但是init不是alloc,new,copy或者類似的方法,因此我們不能釋放從它返回的對象,因為它不會給我們對象的所有權。

init代表其調用者釋放舊對象時,如果它創建一個新對象,它代表其調用者執行此操作。 調用者擁有init為其創建或檢索的替代對象。

作為推論,如果init檢索以前存在的對象,它必須保留它,以便調用者擁有它。

再次檢查假設的* new方法,它還需要釋放舊對象並創建(擁有)或保留替代品。

在所有這些情況下,它init代表其調用者。 對於一種方法來說,進行另一種內存管理通常很狡猾,但對於這些情況, init代表其調用者執行它是必要的。

* new方法確實存在,但只是發送allocinit ,因此不需要單獨實現它。

如果由於某種原因初始化失敗並且必須返回null,那么必須釋放該對象以避免泄漏內存。

類似地,init可能決定交換另一個對象並返回它 - 在這種情況下,您還必須釋放該對象以避免泄漏內存。

在這兩種情況下都是必要的,因為初始對象不是由init返回的,並且在方法返回后將被孤立。 Alloc已經自動保留了對象,因此如果你不釋放它,它的保留計數將永遠停留在1。

[另一個角度會有幫助嗎?] init方法(及其兄弟姐妹initWith ......和類似的東西)有點奇怪,但不是內存分配規則的特例。 Init很奇怪,因為它有一個名稱,聽起來它會改變實例的內部,但實際上它可能會做更多的事情(例如,它可能會替換其他一些對象並初始化對象)。 提示是在init的聲明中:

- (id)  init  // the way it is

VS

- (void) init  // in some other universe

init方法返回一個對象,因此它可能更好地命名為“返回一個對象,它是一個等級對象並且已經初始化”。 大多數方法都不執行這種切換器,這使得init有點不同/奇怪。

關於alloc / init嵌套沒有任何“魔力” - 它只是處理從alloc返回的對象可能與init返回的對象不同的最簡單方法。 這非常好用:

NSSomeClass* s = [NSSomeClass alloc];
s = [s init];  // that 's =' part is really important ;-)

並且完全等同於“標准”習語:

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

這可能存在問題:

NSSomeClass* s = [NSSomeClass alloc]
[s init];   // bad! you ignored the result of init

當實現返回的對象不同於作為傳入的“self”接收的對象時,必須特別小心地執行init方法。 在這種情況下,init方法負責“自我”對象的內存管理(因為它不會返回該對象 - 所以還有誰可以進行管理?)

BTW可能會做一些非常難看的詭計。 不要在家嘗試這個!

// don't do this!
S* s = [S alloc] 
[s retain]; // needs to survive 2 init's
S* ss = [s init......];  // s.retain goes 2-->1
S* sss = [s init.....];  //  ... goes 1-->0;

這樣做非常糟糕,因為它依賴於init .....方法總是返回一個新對象(而不是它接收到的對象)。 這是一個明顯不好的假設!

注意,通過“在'self'中接收對象的方法”,我的意思是該方法是在對象上/由對象調用的,並且該對象通過“self”指針按慣例可用。

基本規則不適用於這種特殊情況。

事實上,不要擔心 - 除非你打算做Posing,否則你不需要編寫執行此操作的代碼,它對你編寫的代碼完全沒有影響。

您應該繼續遵循所有代碼中的基本規則。

第三,代碼比實際規則更像你所謂的“指南”。

(巴博薩船長)

alloc / init是一個特例。 你必須在init中執行保留/釋放內容,使得調用者返回的任何對象都由調用者擁有並且沒有泄漏。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM