2013-07-23 77 views
7

比方說,我想從一個塊中訪問selfARC __block和__weak

[someObject successBlock:^(NSArray *result) { 
    [self someSuccessMethod]; 
} failure:^(NSString *errorMessage, int status) { 
    [self someFailureMethod]; 
}]; 

據我所知,這將創建一個保留週期和someObjectself永遠不會去alloced。

什麼讓我感到困惑的是有沒有__block關鍵字實際發生的情況。我可以通過進行__weak參考自我修復保留週期:

__weak MyClass* me = self; 
[someObject successBlock:^(NSArray *result) { 
    [me someSuccessMethod]; 
} failure:^(NSString *errorMessage, int status) { 
    [me someFailureMethod]; 
}]; 

我不需要在這裏使用__block,因爲我並不想從塊內修改me。據我所知,如果我不使用__block,那麼塊內會引用me的副本。我的問題是:如果塊內引用的內容只是對象的副本,爲什麼原始代碼塊會創建保留週期?我猜想對self的引用只是一個副本,因爲我從來沒有使用__block關鍵字。我是否錯誤地思考這個問題?

回答

7

在第一種情況下,該塊捕獲self,即它將self的副本保存爲另一個指針。這增加了指向對象的保留計數,並導致保留週期。

在第二種情況下,塊捕獲me,即,它節省了me副本作爲另一 指針。這不會增加保留次數,因此不會導致保留週期。

(如果打印地址me外部和塊內,你會看到 地址是不同的。該塊有它自己的弱指針的對象。)

如果pointed-通過Objective-C運行時將所有弱引用(包括由塊保存的那個引用(包括 塊))設置爲nil

(我只希望我有這個權利。)

+0

假設MyCLass實現了一個副本,它是一個真正的副本......因爲'-copyWithZone:'可以保留......這是完全合法的,並且可以在任何不可變對象中完成。 –

+0

@GradyPlayer:或許我表達得很糟糕,但我的意思是塊在塊的上下文中用'self'(或'me')的*當前內容*保存一個強(或弱)指針。不涉及'copy' *方法*。 –

+0

是的,當有人對他們做某些事情時,有時候這樣的循環會回到頂端......有時我必須在幾個月或幾年之後纔有一個書呆子挑選......但是可以在塊捕獲時複製對象,所以我不需要認爲這是不正確的... –

0

您可以路徑自我爲塊的說法,正好給變量名「自我」,這將在塊selfretaining保護。

而你對'someObject和自己從來沒有得到de-alloced':當block被釋放時,self會被釋放。塊將被someObject釋放。 SomeObject將在沒有更多引用時被釋放。所以如果你的self-object擁有someObject,只需在你不再需要的時候釋放someObject即可。

4

A 保留週期發生在兩個對象存儲彼此強關聯時。最簡單的情況是存儲對象a的對象bb的強烈參照做相反的[1]。 Objective-C中保留循環是一個問題,因爲它們讓ARC相信這些對象始終在使用中,即使這些對象沒有從其他地方引用。

讓我們來回顧一些例子。您有分配ab的對象z,利用它們,然後配置它們。如果ab首先在它們自己之間創建了保留循環,則不會釋放其中的ab。如果你多次這樣做,你會嚴重泄漏記憶。

一個保留週期的另一個現實世界的例子是,如果a分配和強烈引用b對象,但你還可以存儲從b在對象圖a(許多更小的物體很強的參考可能需要訪問父母 )。

在這些情況下最常見的解決方案是確保包含的對象只包含對其包含對象的弱引用,並確保兄弟對象不包含彼此的強引用。

另一種解決方案(通常不那麼優雅,但在某些情況下可能適用)可能在a中有某種自定義cleanup方法,其缺少對b的引用。因此b將在調用cleanup時被釋放(如果b未在其他地方被強烈引用)。這很麻煩,因爲你不能從adealloc(如果有保留週期它永遠不會被調用)以及因爲你必須記得在適當的時間呼叫cleanup

  1. 注意,保留週期是也可傳遞(例如,對象a強烈引用b強烈引用c強烈引用a)。

有了這一切說:塊的內存管理是相當棘手的理解。

你的第一個例子中可以創建一個臨時保留週期(且僅當您self對象存儲的強引用someObject)。當該塊完成執行並釋放後,此臨時保留週期將消失。

在執行過程中,self將一個參考someObjectsomeObject存儲到blockblockself一次。但是,這只是暫時的,因爲塊不會永久存儲在任何地方(除非[someObject successBlock:failure:]實現這樣做,但對於完成塊而言不常見)。

因此,在您的第一個示例中,保留週期不是問題。

通常,如果某些對象正在存儲該塊而不是直接執行,則在塊內保留週期只是一個問題。然後很容易看到,self強烈參考block,而block強烈參考self。請注意,從塊中訪問任何ivar會自動生成該塊中的self的強引用。

確保包含的對象沒有強烈引用其容器的等價物是使用__weak SelfClass *weakSelf = self來訪問這兩種方法和ivars(如果通過訪問器訪問ivars,就像使用屬性時一樣)。您的區塊對self的引用將會很弱(它的不是副本,它是弱引用),並且允許self在不再強烈引用時解除分配。

有人可能會爭辯說,在所有塊中都使用weakSelf是好習慣,以防萬一。我想知道爲什麼蘋果沒有把這個默認行爲。這樣做通常不會做任何有害的代碼塊,即使實際上不需要。


__block上指向對象的變量很少使用,因爲Objective-C的不強制喜歡的對象的不變性。

如果您有一個指向該對象的指針,則可以調用它的方法,並且這些方法可以修改它,無論有沒有__block__block更適用於基本類型的變量(int,float等)。有關將對象指針變量用於__block時發生的情況,請參見here。您還可以閱讀Apple的Blocks Programming Topics__block的更多信息。

編輯:修正了在對象指針上使用__block的錯誤。感謝@KevinDiTraglia指出它。

+1

不錯的答案,但你確定最後的陳述嗎?我正在研究使用__block而不是__weak作爲引用類型的問題,它們具有不同的行爲,__weak引用變爲零,而__block引用不變。我認爲它更接近對象引用的強指針。 –

+0

感謝您的評論,你是對的。我修復了這一點。 –

+0

不確定是否對自己總是很弱的引用是正確的。有時我認爲你可能希望該塊保留引用,所以它不會讓它被釋放。據我瞭解,只有在使用強參考時纔會使用,這會導致保留週期。 – Ixx

3

你的第一個例子不會創建一個從不結束保留週期。將會有保留週期,所有權利,但一旦塊完成後,將刪除對someObject的塊的引用。所以someObject將至少生活,直到塊完成。 這種暫時保留週期可能是一個好事還是壞事,取決於你想要什麼:

如果您需要someObject存活至少直到其塊的完成,也沒關係。但是,如果沒有理由保留該對象,則應使用「弱」引用來實現它。

例如, myObject是一個視圖控制器,它在這些塊中從網絡中獲取圖片。如果您從導航控制器彈出someObject,則控制器在取回後將無法顯示圖片,因此無需保留該圖片。成功或錯誤是無關緊要的,用戶不再對someObject應該獲取的圖片感興趣。在這種情況下,弱的使用是更好的選擇,但是塊中的代碼應該預期爲self可能爲零。

+1

說一旦塊完成後,對* them *的引用就被刪除了嗎? – Ixx

+0

這確實是正確的。 +1,因爲它解釋了爲什麼它不會創建一個perm保留週期。許多新的程序員總是使用weakSelf,因爲他們在接受的答案中列出的保留週期上誤導了他們。雖然這對大多數應用程序來說都是正常的,但是如果您稍後嘗試引用這些對象,那麼更復雜的應用程序將會看到引用在塊被執行之前會被釋放的問題,從而導致崩潰。我想你的意思是在最後一句話中說「weakSelf」可能是零。 – Bot

相關問題