2011-11-11 41 views
14

我試圖保留一個塊的引用,這個塊被一個方法傳遞給我的類,稍後調用。但是,我遇到了麻煩,請保留一個參考。塊在NSDictionary(ARC)中被釋放

我認爲,顯而易見的方法是將其添加到伊娃集合中,所有這些都應該保持對其內容的強烈參考。但是當我試圖把它拉回來時,它是零。

的代碼非常簡單:

typedef void (^DataControllerCallback)(id rslt); 

@interface DataController : NSObject { 
    NSMutableArray* queue; 
} 
- (void) addBlock:(DataControllerCallback)callback; 
- (void) functionToBeCalledLater; 
@end 

@implementation DataController 

- (id) init { 
    self = [super init]; 
    if (self != nil) {   
     queue = [NSMutableArray new]; 
    } 
    return self; 
} 

- (void) addBlock:(DataControllerCallback)callback { 
    NSDictionary* toAdd = [NSDictionary dictionaryWithObjectsAndKeys: 
     [callback copy], @"callback", 
     @"some other data", @"data", nil]; 
    [queue addObject:toAdd]; 
} 

- (void) functionToBeCalledLater { 
    NSDictionary* dict = [queue lastObject]; 
    NSLog(@"%@", [dict objectForKey:@"data"]; //works 
    DataControllerCallback callback = [dict objectForKey:@"callback"]; //this is nil 
    callback(@"an arguemnt"); //EXC_BAD_ACCESS 
} 

發生了什麼事?


更新:我已經與[callback copy]嘗試過了,只是callback插入字典,既不作品。


更新2:如果我只是堅持我的塊成的NSMutableSet,只要我打電話copy,我沒事。它效果很好。但是,如果它在一個NSDictionary中,它不會。

我實際上已經通過在創建NSDict之後放置一個斷點來測試它,並且回調永遠不會被插入。該描述清楚地表明「1個鍵值對」,而不是兩個。

我目前正在解決這個與專門的類,只是作爲一個容器。 callback財產宣佈爲strong;我甚至不需要使用copy

但問題仍然存在:爲什麼會發生這種情況?爲什麼NSDictionary不存儲塊?這是否與iOS 4.3的目標有關,因此ARC必須作爲靜態庫來構建?


更新3:女士們,先生們:我是個白癡。

我在這裏介紹的代碼顯然是實際代碼的簡化版本;最特別的是,它將一些鍵/值對留在字典中。

如果您使用[NSDictionary dictionaryWithObjectsAndKeys:]存儲一個NSDictionary一個,你可要該死確保這些值的一個不nil

其中之一是。

ICYMI,它導致參數列表提前終止。我有一個userInfo類型的參數被傳入「add to queue」方法之一,當然,你可以傳入「nil」。然後,當我構造字典時,夾住這個參數導致構造函數認爲我已經終止了參數列表。 @"callback"是字典構造函數中的最後一個值,它永遠不會被存儲。

+0

我複製並粘貼了這個代碼[callback copy],它一切正常。 –

+0

'functionToBeCalledLater'出現在運行循環的不同迭代中,儘管我認爲這並不重要,但也許它與ARC放置其保留和釋放的位置有關。 –

+0

我在runloop的不同迭代中調用了functionToBeCalledLater(爲它做了一個小按鈕,事實上多次敲擊它) –

回答

31

與流行的錯誤概念相反,ARC 不會自動將堆棧作爲參數傳遞給方法。它只從方法/函數返回塊時自動解除堆疊。

I.e.這個....

[dict setObject: ^{;} forKey: @"boom"]; 

...如果dict倖存超出了範圍,您嘗試使用該塊(實際上會崩潰,也不會在這種情況下,因爲這是一個靜態塊,但這是一個編譯器的細節,你不能依靠)。

這是documented here

如何塊ARC工作?

當您在ARC模式下傳遞堆棧上的塊時,塊「正常工作」,例如返回中的 。你不必再打電話給Block Copy。 您 仍然需要使用[^ {}副本]路過的時候「向下」堆棧到 arrayWithObjects:和其他方法,只要一個保留。

返回值行爲可以自動化,因爲它是總是正確返回一個堆基於塊(總是和一個錯誤返回一個基於堆棧的塊)。在一個塊作爲-一個參數的情況下,這是不可能的方式,將是既非常有效的,總是正確的自動化的行爲。

分析儀可能應該警告這個用途。如果沒有,請提交錯誤。

(I derped一個當我指的很抱歉,。。)


編譯器不幾個原因自動化塊-AS-參數:

  • 不必要地將塊複製到堆可能會造成顯着的性能損失
  • 塊的多個副本可以將該性能懲罰ty顯着。

即:

doSomethingSynchronous(aBlock); 
doSomethingSynchronous(aBlock); 
doSomethingSynchronous(aBlock); 
doSomethingSynchronous(aBlock); 

如果是暗示4 Block_copy()操作和ABLOCK包含捕獲狀態的顯著量,這會是一個巨大的潛在衝擊。

•只有這麼多時間在白天和自動化的參數的處理是充滿了不常見的邊緣情況。如果這是在未來的自動處理,它可以在不破壞現有的代碼來完成,因此,也許這將是在未來完成。

I.e.編譯器可以生成:

aBlock = [aBlock copy]; 
doSomethingSynchronous(aBlock); 
doSomethingSynchronous(aBlock); 
doSomethingSynchronous(aBlock); 
doSomethingSynchronous(aBlock); 
[aBlock release]; 

這不僅修復塊作爲-PARAM的問題,但它也只會在所有可能的用途產生了塊的一個副本。


的問題仍然有效,但:爲什麼會這樣?爲什麼不會有一個 NSDictionary存儲塊?它是否與我針對iOS 4.3的事實 有關,因此ARC必須內置爲靜態 庫?

那麼奇怪的事情正在發生。巧合的是,我上週在一個基於ARC的應用程序中使用了塊作爲值,並且工作正常。

你有一個簡單的例子嗎?

+0

那麼,你的回答顯然比我的好= = +1 – justin

+1

返回一個基於棧的塊總是正確的,並且返回一個基於棧的塊總是錯誤的?我懷疑其中一個應該說'堆'。 –

+0

'返回一個基於棧的塊總是正確的(並且總是返回一個基於棧的塊的錯誤)「,在第一種情況下不應該是_static_ block嗎? – 2011-11-11 06:58:54