块获取释放,而在NSDictionary(ARC)

我试图保留一个Block的引用,这个Block被一个方法传递给我的类,稍后再调用。 我遇到了麻烦,但是保持对它的引用。

我认为,显而易见的方法是将其添加到伊娃集合中,所有这些都应该对其内容保持强有力的引用。 但是,当我试图把它拉回来,它是零。

代码非常简单:

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,它不。

我实际上已经通过在创buildNSDict之后放置一个断点来testing它,并且callback永远不会被插入。 描述清楚地读出“1个键值对”,而不是两个。

我目前正在用一个专门的类来解决这个问题,这个类只是一个容器。 callback属性声明为strong ; 我甚至不需要使用copy

但问题仍然存在:为什么会这样呢? 为什么NSDictionary不能存储一个块? 这是否与我的目标iOS 4.3的事实有关,因此ARC必须作为一个静态库内置?


更新3:女士们,先生们:我是个白痴。

我在这里介绍的代码显然是实际代码的简化版本; 最特别的是,它将一些键/值对留在字典中。

如果您使用[NSDictionary dictionaryWithObjectsAndKeys:]存储在NSDictionary中,那么最好确保其中的一个值不nil

其中之一是。

ICYMI,这是导致争议名单的提前终止。 我有一个userInfotypes的参数被传递到“添加到队列”方法之一,你当然可以通过“无”。 然后当我构造字典时,夹住这个参数导致构造函数认为我已经终止了参数列表。 @"callback"是字典构造函数中的最后一个值,它永远不会被存储。

与stream行的错误概念相反,ARC 不会自动将作为parameter passing的块去堆栈 。 当方法/函数返回一个块时,它只会自动解除堆栈。

即这个….

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

如果dict超出了范围,并且尝试使用该块(实际上,在这种情况下不会因为这是一个静态块,但是这是您不能依赖的编译器细节),将会崩溃。

这是在这里logging :

ARC如何工作?

当你在ARC模式下传递数据块时,块“正常工作”,比如返回。 您不必再调用Block Copy。 在向下传递堆栈到arrayWithObjects:和其他保留方法时,您仍然需要使用[^ {} copy]。

返回值行为可以是自动的,因为返回一个基于堆的块总是正确的(总是返回一个基于堆栈的块的错误)。 在块作为参数的情况下,不可能以既高效又总是正确的方式来自动化行为。

分析仪可能应该警告这种用法。 如果没有,请提交一个错误。

(当我意思是堆的时候,我一堆东​​西 ,对不起。)


由于几个原因,编译器不会自动将块作为参数:

  • 不必要的复制一个块到堆可能是一个重大的性能损失
  • 块的多个副本可以显着地增加性能损失。

即:

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

如果这意味着四个Block_copy()操作和一个Block包含大量的被捕获状态,那将是一个巨大的潜在命中。

•白天只有这么多小时,自动处理参数的情况非常普遍。 如果将来自动处理,可以在不破坏现有代码的情况下完成,因此可能在将来完成。

即编译器可以生成:

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

这不仅可以解决param-as-param的问题,而且在所有潜在的用途上也只会产生一个块的副本。

但问题仍然存在:为什么会这样呢? 为什么NSDictionary不能存储一个块? 这是否与我的目标iOS 4.3的事实有关,因此ARC必须作为一个静态库内置?

那么奇怪的事情正在发生。 巧合的是,我在上个星期在一个基于ARC的应用程序中使用了块作为值,并且工作正常。

你有一个简单的例子吗?