[英]Can using __weak attribute to pass parameter to blocks lead to memory leaks?
在我支持iOS ARC的代碼中,我需要將“self”和其他對象傳遞給一個塊。 更具體地說,我需要在ASIHTTPRequest
的completionBlock
與self和ASIHTTPRequest
對象進行交互。
_operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(parseServerReply) object:nil];
_request = [ASIHTTPRequest requestWithURL:@"server.address"];
// ...
[_request setCompletionBlock:^{
[self setResponseString:_request.responseString];
[[MyAppDelegate getQueue] addOperation:_operation];
}];
為了避免以下警告: Capturing "self" strongly in this block will likely lead to a retain cycle
。 我修改了我的代碼,以便在此帖子后面的塊中使用的對象添加__weak
屬性: 修復警告“在此塊中強烈捕獲[一個對象]可能會導致保留周期”在啟用ARC的代碼中
結果代碼是:
_operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(parseServerReply) object:nil];
_request = [ASIHTTPRequest requestWithURL:@"server.address"];
// ...
__weak NSOperation * operation = _operation;
__weak ASIHTTPRequest * request = _request;
__weak typeof(self) * self_ = self;
[request setCompletionBlock:^{
[self_ setResponseString:request.responseString];
[[MyAppDelegate getQueue] addOperation:operation];
}];
我想知道這是否仍會導致保留周期和內存泄漏。 如果是這樣,有沒有辦法避免泄漏?
您不需要使所有內容都變弱,也不想繼續引用塊中的弱對象(特別是如果塊可以在不同的線程中執行)。
這樣想吧。 使用ARC時,對象將被引用計數。 在引用計數變為零之前,不會在對象上調用dealloc。 因此,只要有一個引用,對象就會保持活力。
但是,請考慮兩個具有強引用的對象。 在另一個發布其引用之前,它們都不會被釋放。
創建塊時,它會捕獲其環境,這意味着它將創建對塊范圍內使用的任何對象的強引用。
考慮到這一點...
id object = getMeSomeObject();
// <object> is implicitly __strong, and now has a reference.
在釋放所有強引用之前,該對象不會獲得dealloc。 如果在塊中使用它,則塊會自動創建自己的強引用,以確保只要塊存在,對象就會存在。
__weak引用是一個間接級別,使您可以訪問長期作為對象的對象。 基本上,如果將對象分配給__weak指針,則只要該對象處於活動狀態,該指針就可以保證為您提供相同的對象。 一旦對象啟動它自己的dealloc(),它就會找到所有__weak指針並將它們設置為nil。
因此,_weak指針將始終處於兩種狀態之一。 只要對象存在,它就指向一個有效的對象,或者當對象具有dealloc時它指向nil。 你永遠不應該通過弱指針訪問一個對象,因為對象可以在你的背后釋放,留下一個壞指針。
所以,你想要做的是在棧上創建一個__strong引用,這樣只要你想要它,對象就會保持活着狀態。
在你的情況下......
[_request setCompletionBlock:^{
[self setResponseString:_request.responseString];
[[MyAppDelegate getQueue] addOperation:_operation];
}];
這個區塊顯然對self
有很強的借鑒意義。 你可能不希望這樣。 我們試着解決它......
// weakSelf will be "magically" set to nil if <self> deallocs
__weak SelfType *weakSelf = self;
[_request setCompletionBlock:^{
// If <self> is alive right now, I want to keep it alive while I use it
// so I need to create a strong reference
SelfType *strongSelf = weakSelf;
if (strongSelf) {
// Ah... <self> is still alive...
[strongSelf setResponseString:_request.responseString];
[[MyAppDelegate getQueue] addOperation:_operation];
} else {
// Bummer. <self> dealloc before we could run this code.
}
}];
嘿,我們現在有一個弱小的self
,但是......你仍然會遇到同樣的問題。 為什么? 因為_request和_operation是實例變量。 如果訪問塊內的實例變量,它會隱式創建對self
的強引用。
給它另一個去......
// weakSelf will be "magically" set to nil if <self> deallocs
__weak SelfType *weakSelf = self;
[_request setCompletionBlock:^{
// If <self> is alive right now, I want to keep it alive while I use it
// so I need to create a strong reference
SelfType *strongSelf = weakSelf;
if (strongSelf) {
// Ah... <self> is still alive...
[strongSelf setResponseString:strongSelf->_request.responseString];
[[MyAppDelegate getQueue] addOperation:strongSelf->_operation];
} else {
// Bummer. <self> dealloc before we could run this code.
}
}];
現在,您可能不應該在“原始”中使用實例變量,但這是一個不同的主題。
通過這些更改,您將擁有一個不再具有對self的強引用的塊,並且如果self
確實是dealloc,則它會優雅地處理它。
最后,我要重申一下,對strongSelf的賦值是必要的,以防止在檢查weakSelf后對象消失的潛在問題。 特別...
if (weakSelf) {
// Hey, the object exists at the time of the check, but between that check
// and the very next line, its possible that the object went away.
// So, to prevent that, you should ALWAYS assign to a temporary strong reference.
[weakSelf doSomething];
}
strongSelf = weakSelf;
// OK, now IF this object is not nil, it is guaranteed to stay around as long as
// strongSelf lives.
現在,在這種情況下,塊是請求的一部分,它是self
一部分,因此self
deallocs的可能性很小,但我的主要觀點是使用self來阻止保留周期,但仍然總是通過一個訪問對象強烈的參考 - 弱強的舞蹈。
您生成的代碼是安全的。 閱讀Brad Larson的答案,了解具體細節。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.