我正在尝试保留对通过方法传递给我的类的块的引用,以便稍后调用。但是,我很难保持对它的引用。
我认为,显而易见的方法是将其添加到ivar集合中,所有这些都应该保持对其内容的强烈引用。但是当我试图把它拉回去时,它就没有了。
代码非常简单:
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中存储值,则最好该死的确定其中一个值不是{{1 }}
其中一个是。
ICYMI,它导致提前终止参数列表。我有一个userInfo类型参数被传递到“添加到队列”方法之一,当然,你可以传入“nil”。然后当我构造字典时,在该参数中查看导致构造函数认为我已经终止了参数列表。 nil
是字典构造函数中的最后一个值,它从未被存储过。
答案 0 :(得分:31)
与流行的错误概念相反,ARC 不会自动对作为方法的参数传递的块进行反堆叠。它仅在从方法/函数返回块时自动解除堆栈。
即。此....
[dict setObject: ^{;} forKey: @"boom"];
...如果dict
超出范围并且您尝试使用该块会崩溃(实际上,在这种情况下它不会因为这是一个静态块,但这是一个编译器细节,你不能依赖)。
块如何在ARC中工作?
在ARC模式下将块传递到堆栈时阻止“正常工作”,例如 在回报中。您不必再调用Block Copy。 您 当将“堆栈”传递给时,仍然需要使用[^ {} copy]
arrayWithObjects:
以及其他保留的方法。
返回值行为可以自动化,因为它始终正确以返回基于堆的块(并且总是返回基于堆栈的块的错误)。在作为参数的块的情况下,不可能以非常有效且始终正确的方式自动化行为。
分析仪可能应该警告过这种用法。如果没有,请提交错误。
(当我的意思是堆时,我发现了堆栈。很抱歉。)
由于以下几个原因,编译器不会自动执行block-as-parameters:
即:
doSomethingSynchronous(aBlock);
doSomethingSynchronous(aBlock);
doSomethingSynchronous(aBlock);
doSomethingSynchronous(aBlock);
如果这意味着四个Block_copy()操作并且aBlock包含大量捕获状态,那将是一个巨大的潜在打击。
•当天只有这么多小时,并且参数处理的自动化充满了非明显的边缘情况。如果将来自动处理,可以在不破坏现有代码的情况下完成,因此可能会在将来完成。
即。编译器可以生成:
aBlock = [aBlock copy];
doSomethingSynchronous(aBlock);
doSomethingSynchronous(aBlock);
doSomethingSynchronous(aBlock);
doSomethingSynchronous(aBlock);
[aBlock release];
这不仅会解决block-as-param的问题,而且还会在所有潜在用途中生成该块的一个副本。
那时候有些奇怪的事情发生了。巧合的是,我上周在基于ARC的应用程序中使用了块值作为值,并且工作正常。但问题仍然存在:为什么会发生这种情况?为什么不会 NSDictionary存储一个块?它与事实有关吗? 我的目标是iOS 4.3,因此ARC必须作为静态内置 库中?
你有一个方便的小例子吗?