[英]ARC __block and __weak
假設我正在嘗試從塊內訪問self
:
[someObject successBlock:^(NSArray *result) {
[self someSuccessMethod];
} failure:^(NSString *errorMessage, int status) {
[self someFailureMethod];
}];
我知道這會創建一個保留周期,而someObject
和self
永遠不會被解除分配。
令我困惑的是,在沒有__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
存儲對象b
和b
的強引用[1]。 保留周期是Objective-C中的一個問題,因為它們使ARC相信這些對象總是在使用,即使這些對象不是從其他任何地方引用的。
我們來看一些例子。 你有對象z
,它分配a
和b
,使用它們,然后處理它們。 如果a
和b
首先在它們之間創建了一個保留周期,則a
和b
將不會被釋放。 如果你多次這樣做,你會嚴重泄漏記憶。
一個保留周期的另一個現實世界的例子是,如果a
分配和強烈引用b
的對象,但你也從存儲很強的參考b
到a
(對象圖中的許多較小的對象可能需要訪問他們的父母 )。
在這些情況下,最常用的解決方案是確保包含的對象僅包含對其包含對象的弱引用,並確保兄弟對象不包含對彼此的強引用。
另一種解決方案(一般少優雅,但在某些情況下可能是適當的)可被具有某種自定義的cleanup
方法在a
該NILS其參照b
。 因此,當調用cleanup
時, b
將被釋放(如果b
在其他地方沒有被強烈引用)。 這很麻煩,因為你不能從a
dealloc
做到這一點(如果有一個保留周期則永遠不會被調用),並且你必須記得在適當的時候調用cleanup
。
a
強烈引用b
強烈引用c
強烈引用a
)。 所有這些都說:塊的內存管理非常難以理解。
您的第一個示例可以創建一個臨時保留周期(僅當您的self
對象存儲對someObject
的強引用時)。 當塊完成執行並被解除分配時,此臨時保留周期消失。
在執行期間, self
將存儲參考someObject
, someObject
到block
,和block
到self
試。 但同樣,它只是暫時的,因為塊不是永久存儲在任何地方(除非[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.