簡體   English   中英

ARC __block和__weak

[英]ARC __block and __weak

假設我正在嘗試從塊內訪問self

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

我知道這會創建一個保留周期,而someObjectself永遠不會被解除分配。

令我困惑的是,在沒有__block關鍵字的情況下實際發生了什么。 我可以通過對self進行__weak引用來修復保留周期:

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

我不需要在這里使用__block ,因為我不是想在塊內修改me 根據我的理解,如果我不使用__block ,則在塊內引用me的副本。 我的問題是:如果塊中引用的內容只是對象的副本,為什么原始代碼塊會創建保留周期? 我猜想對self的引用只是一個副本,因為我從不使用__block關鍵字。 我錯誤地想到了這個嗎?

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

在第二種情況下,塊捕獲me ,即它將me的副本保存為另一個指針。 這不會增加保留計數,因此不會導致保留周期。

(如果在塊的外部和內部打印me地址 ,您將看到地址不同。塊具有自己的指向對象的弱指針。)

如果取消分配指向的對象,則Objective-C運行時將所有弱引用(包括塊保存的引用)設置為nil

(我只希望我做對了。)

當兩個對象彼此存儲強引用時,會發生保留周期 最簡單的情況是對象a存儲對象bb的強引用[1]。 保留周期是Objective-C中的一個問題,因為它們使ARC相信這些對象總是在使用,即使這些對象不是從其他任何地方引用的。

我們來看一些例子。 你有對象z ,它分配ab ,使用它們,然后處理它們。 如果ab首先在它們之間創建了一個保留周期,則ab將不會被釋放。 如果你多次這樣做,你會嚴重泄漏記憶。

一個保留周期的另一個現實世界的例子是,如果a分配和強烈引用b的對象,但你也從存儲很強的參考ba (對象圖中的許多較小的對象可能需要訪問他們的父母 )。

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

另一種解決方案(一般少優雅,但在某些情況下可能是適當的)可被具有某種自定義的cleanup方法在a該NILS其參照b 因此,當調用cleanup時, b將被釋放(如果b在其他地方沒有被強烈引用)。 這很麻煩,因為你不能從a dealloc做到這一點(如果有一個保留周期則永遠不會被調用),並且你必須記得在適當的時候調用cleanup

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

所有這些都說:塊的內存管理非常難以理解。

您的第一個示例可以創建一個臨時保留周期(僅當您的self對象存儲對someObject的強引用時)。 當塊完成執行並被解除分配時,此臨時保留周期消失。

在執行期間, self將存儲參考someObjectsomeObjectblock ,和blockself試。 但同樣,它只是暫時的,因為塊不是永久存儲在任何地方(除非[someObject successBlock:failure:]實現這樣做,但這對於完成塊來說並不常見)。

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

通常,如果某個對象存儲塊而不是直接執行它,則塊內的保留周期只是一個問題。 然后很容易看出self強烈地引用了block ,並且block具有對self的強烈引用。 請注意,從塊內部訪問任何ivar會自動在該塊中生成對self的強引用。

相當於確保包含的對象不強引用其容器使用__weak SelfClass *weakSelf = self來訪問方法和ivars(如果通過訪問器訪問ivars,則使用屬性時更好)。 你的塊對self的引用會很弱(它不是副本 ,它是一個弱引用 ),並且當它不再被強烈引用時,它將允許self解除分配。

可以說,為了weakSelf ,總是在所有塊中使用weakSelf ,存儲與否,這是一個好習慣。 我想知道為什么Apple沒有將此作為默認行為。 這樣做通常不會對塊代碼做任何有害的事情,即使實際上不需要。


__block很少用於指向對象的變量,因為Objective-C不強制執行這樣的對象的不變性。

如果你有一個指向對象的指針,你可以調用它的方法,這些方法可以修改它,有或沒有__block __block對基本類型的變量(int,float等)有用(僅?)。 請參閱此處了解將__block與對象指針變量一起使用時會發生什么。 您還可以在Apple的Blocks Programming Topics中閱讀有關__block更多信息。

編輯:修復了有關對象指針的__block用法的錯誤。 感謝@KevinDiTraglia指出它。

您的第一個示例不會創建永不停止的保留周期。 可以保留循環,但是一旦塊完成,將刪除對someObject的塊的引用。 所以someObject將至少存在,直到塊完成。 這種臨時保留周期可能是好事還是壞事,取決於你想要的:

如果你需要你的someObject至少活着,直到它的塊完成,它沒關系。 但是,如果沒有理由保留該對象,則應使用“弱”引用來實現它。

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

你可以將self作為塊的參數路徑,正確地給出變量名'self',這將防止在塊中自我保留。

並且你對'someObject和self永遠不會被取消分配'是錯誤的:當釋放塊時,self將被釋放。 塊將被someObject釋放。 當SomeObject沒有更多引用時,它將被釋放。 因此,如果您的自我對象擁有someObject,則只需在不再需要時釋放someObject。

暫無
暫無

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

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