2013-02-22 43 views
13

如果我發送消息給弱對象會發生什麼?發送消息是否擁有該對象並將其保存在內存中直到返回?消息__weak對象?

我想這個模式:

__weak MyObject *weakSelf = self; 
dispatch_async(dispatch_get_main_queue(), ^{ 
    [weakSelf doSomeAction]; 
}); 

假設weakSelf是非零發送消息時,它可能會被同時doSomeAction正在釋放或者是保證仍然有效,直到doSomeAction回報?

回答

18

Clang ARC documentation

執行上的對象的左值一個左值到右值轉換時,會發生。

  • 對於__weak對象,當前的指針對象被保持並且然後在當前滿表達的端部釋放。這必須以原子方式執行任務和指出對象的最終版本。

消息傳送的弱引用對所述可變左值到右值的轉換,這意味着弱引用的值將被保留,然後在當前滿表達的端部(釋放基本上該聲明)。它基本上等同於分配給一個強大的變量,其範圍只持續當前的語句,然後傳遞這個強大的變量。

這裏的東西是,如果你想要一次發送一個弱變量並且再也不要觸及它,並且你不關心在弱引用結束的情況下評估該方法的參數的副作用up nil,然後繼續直接發送弱參考。但是如果您需要兩次(在單獨的語句中)引用弱引用,或者評估參數的副作用確實很重要,那麼您應該在繼續之前指定一個強變量並測試非nil

+0

這似乎對我來說是一個實現的怪癖;我懷疑這是記錄/保證任何地方。 :) – 2013-02-23 18:33:57

+0

@Rob:這幾乎肯定是Steven說的一個實現問題。編譯器可能使用'objc_loadWeak()',它自動釋放該值,而不是'objc_loadWeakRetained()',而不是'objc_loadWeakRetained()'。你不能依賴這種行爲,因爲它可能隨着編譯器的優化級別或將來版本而改變。 – 2013-02-23 20:28:14

+0

後續問題:那麼蘋果在LLVM 5.0中提供了「發送消息給__weak指針」警告的潛在問題?我注意到「反覆使用__weak引用」是一個單獨的警告。除非在優化參數之前進行'nil'檢查的優化(即它不僅發生在'objc_msgSend'中)? – Tommy 2013-10-29 21:34:38

4

你問:

假設weakSelf是非nil發送消息時,它可能會被同時doSomeAction正在釋放或者是保證仍然有效,直到doSomeAction回報?

是的,它不僅保持有效,直到doSomeAction返回,它也保留在塊的其餘部分。考慮以下幾點:

- (void)dealloc 
{ 
    NSLog(@"%s", __FUNCTION__); 
} 

- (void)startBackgroundOperation 
{ 
    __weak MyObject * weakSelf = self; 

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
     [weakSelf doSomeAction]; 
     sleep(5); 
     [weakSelf doSomeAction2]; 
    }); 

    sleep(1); 
} 

- (void)doSomeAction 
{ 
    NSLog(@"%s", __FUNCTION__); 
} 

- (void)doSomeAction2 
{ 
    NSLog(@"%s", __FUNCTION__); 
} 

在這個例子中,我確保對象在範圍上,當塊開始,但讓它掉下來的doSomeActiondoSomeAction2之間的範圍,但該塊似乎保留它的該塊的完成。但是,如果我註釋掉doSomeAction的調用,那麼weak的引用是nil,直到達到doSomeAction2時爲止,就像您期望的那樣。


順便說一句,在WWDC 2011 - #322 - Objective-C Advancements In-Depth,他們指出(約27:00分鐘到視頻),他們指出,如果你提領weakSelf,你應該有調度塊內的局部強引用保護自己的競爭條件,即:

__weak MyClass *weakSelf = self; 

dispatch_async(dispatch_get_main_queue(), ^{ 
    MyClass *strongSelf = weakSelf; 
    if (strongSelf) 
     [strongSelf->myView doSomeViewAction]; 
}); 

這將保留它的塊的時間(假設它是不是已經被執行該塊時釋放)。

+0

你能證明這一點嗎?迄今爲止的證據表明你錯了,發送一條消息給一個弱對象在消息持續期間持有一個引用。 – 2013-02-23 05:48:49

+0

@StevenFisher我會更進一步。向弱對象發送消息不僅保存該消息的持續時間,而且保持整個塊。但是,如果取消引用ivars,它將不會爲您提供引用,因此建議使用本地強引用。我相應地修改了我的答案。 – Rob 2013-02-23 14:33:02

+0

好多了,謝謝。 :) – 2013-02-23 17:42:24