块获取释放,而在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的应用程序中使用了块作为值,并且工作正常。
你有一个简单的例子吗?