Block在NSDictionary(ARC)中发布 [英] Block gets released whilst in NSDictionary (ARC)
问题描述
我正在尝试保留对通过方法传递给我的类的块的引用,以便稍后调用。然而,我遇到了麻烦,保持对它的引用。
I'm trying to retain a reference to a Block that's been passed in to my class by a method, to call at a later time. I'm having trouble, however, maintaining a reference to it.
我想,显而易见的方法是将它添加到ivar集合中,所有这些都是假设的保持对其内容的强烈引用。但是当我试图把它拉出来时,它是零。
The obvious way, I thought, was to add it to an ivar collection, all of which are supposed to maintain strong references to their contents. But when I try to pull it back out, it's nil.
代码非常简单:
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
}
发生了什么事?
更新:我用 [回调副本]
并且只是回调
插入字典中,都不起作用。
Update: I've tried it with [callback copy]
and just callback
inserting into the dictionary, neither works.
更新2:如果我只是将我的块粘贴到NSMutableSet中,只要我调用 copy
,我就没问题。它很有用。但是如果它在NSDictionary中,它没有。
Update 2: If I just stick my block into an NSMutableSet, as long as I call copy
, I'm fine. It works great. But if it's in an NSDictionary, it doesn't.
我实际上通过在创建NSDict之后放置一个断点并且回调永远不会被插入来测试它。描述清楚地写着1键-value pair,而不是两个。
I've actually tested it by putting a breakpoint right after the NSDict is created and the callback never gets inserted. The description reads clearly "1 key-value pair", not two.
我现在用一个专门的类来解决这个问题,它只是一个容器。回调
property声明为强
;我甚至不需要使用复制
。
I'm currently getting around this with a specialised class that just acts as a container. The callback
property is declared as strong
; I don't even need to use copy
.
但问题仍然存在:为什么会发生这种情况?为什么NSDictionary不会存储块?它是否与我以iOS 4.3为目标的事实有关,因此ARC必须作为静态库构建?
The question still stands, though: why is this happening? Why won't an NSDictionary store a Block? Does it have something to do with the fact that I'm targeting iOS 4.3 and thus ARC must be built in as a static library?
更新3:女士们,先生们:我是个白痴。
Update 3: Ladies and gentleman: I am an idiot.
我在这里介绍的代码显然是实际代码的简化版本;最特别的是,它将一些键/值对从字典中删除。
The code I presented here was obviously a simplified version of the actual code; most particularly, it was leaving some key/value pairs out of the dictionary.
如果您使用<在NSDictionary中存储值 code> [NSDictionary dictionaryWithObjectsAndKeys:] ,你最好该死的确定其中一个值不是 nil
。
If you're storing a value in an NSDictionary using [NSDictionary dictionaryWithObjectsAndKeys:]
, you had better be damn sure one of those values isn't nil
.
其中一个是。
ICYMI,它导致提前终止参数列表。我有一个userInfo类型参数被传递到添加到队列方法之一,当然,你可以传入nil。然后当我构造字典时,在该参数中查看导致构造函数认为我已经终止了参数列表。 @callback
是字典构造函数中的最后一个值,它从未存储过。
ICYMI, it was causing an early termination of the argument list. I had a userInfo-type argument being passed into one of the "add to queue" methods, and you could, of course, pass in "nil". Then when I constructed the dictionary, chucking in that argument caused the constructor to think I had terminated the argument list. @"callback"
was the last value in the dictionary constructor and it was never being stored.
推荐答案
与流行的错误概念相反,ARC 不会自动对作为方法的参数传递的块进行反堆叠。当从方法/函数返回一个块时,它只会自动解除堆栈。
Contrary to popular mis-conception, ARC does not automatically de-stackify Blocks passed as arguments to methods. It only de-stackify's automatically when a block is returned from a method/function.
I.e。这....
I.e. this....
[dict setObject: ^{;} forKey: @"boom"];
...如果 dict
幸存将会崩溃超出范围并且您尝试使用该块(实际上,在这种情况下它不会因为它是一个静态块,但这是一个您不能依赖的编译器细节)。
... will crash if dict
survives beyond the scope and you attempt to use the block (actually, it won't in this case because that is a static block, but that is a compiler detail that you can't rely on).
此处记录:
块如何在ARC中工作?
在ARC模式下将块传递给堆栈时块正常工作,如返回时的
。您不必再调用Block Copy。 你
在将堆栈向下传递到
arrayWithObjects:
以及其他方法时仍然需要使用[^ {} copy]保留。
Blocks "just work" when you pass blocks up the stack in ARC mode, such
as in a return. You don’t have to call Block Copy any more. You
still need to use [^{} copy] when passing "down" the stack into
arrayWithObjects:
and other methods that do a retain.
返回值行为可以自动化,因为总是正确到返回基于堆的块(并且总是返回基于堆栈的块的错误)。在以块为参数的情况下,不可能以非常有效且始终正确的方式自动化行为。
The return value behavior could be automated because it is always correct to return a heap based block (and always an error to return a stack based block). In the case of a block-as-an-argument, it is impossible to automate the behavior in a way that would be both very efficient and always correct.
分析器可能应该警告这种用法。如果没有,请提交一个错误。
The analyzer likely should have warned about this use. If it didn't, file a bug.
(当我指的是堆时,我发现了堆栈。抱歉。)
(I derped a stack when I meant a heap. Sorry about that.)
由于以下几个原因,编译器不会自动执行block-as-parameters:
The compiler doesn't automate blocks-as-parameters for a few reasons:
- 不必要地将块复制到堆中可能会造成严重的性能损失
- 块的多个副本可以显着增加性能损失。
即:
doSomethingSynchronous(aBlock);
doSomethingSynchronous(aBlock);
doSomethingSynchronous(aBlock);
doSomethingSynchronous(aBlock);
如果这意味着四个Block_copy()操作并且aBlock包含大量捕获状态,那么这将是一个巨大的潜在影响。
If that were to imply four Block_copy() operations and aBlock contained a significant quantity of captured state, that'd be a huge potential hit.
•当天只有这么多小时,并且参数处理的自动化充满了非明显的边缘情况。如果将来自动处理,可以在不破坏现有代码的情况下完成,因此,将来可能会完成。
• There are only so many hours in the day and automating the handling of parameters is rife with non-obvious edge cases. If this were handled automatically in the future, it could be done without breaking existing code and, thus, maybe it will be done in the future.
I.e。编译器可以生成:
I.e. the compiler could generate:
aBlock = [aBlock copy];
doSomethingSynchronous(aBlock);
doSomethingSynchronous(aBlock);
doSomethingSynchronous(aBlock);
doSomethingSynchronous(aBlock);
[aBlock release];
这不仅可以修复block-as-param的问题,而且还会产生所有潜在用途的一个副本。
Not only would this fix the problem of a block-as-param, but it would also only produce one copy of the block across all potential uses.
问题仍然存在:为什么会发生这种情况?为什么
NSDictionary不会存储块?它是否与我以iOS 4.3为目标的事实
有关,因此ARC必须作为静态
库构建?
The question still stands, though: why is this happening? Why won't an NSDictionary store a Block? Does it have something to do with the fact that I'm targeting iOS 4.3 and thus ARC must be built in as a static library?
然后有些奇怪的事情发生了。巧合的是,我上周在基于ARC的应用程序中使用了块值作为值并且工作正常。
Something bizarre is going on, then. Coincidentally, I've been using blocks-as-values in an ARC based application in the last week and it is working fine.
你有一个方便的最小例子吗?
Do you have a minimal example handy?
这篇关于Block在NSDictionary(ARC)中发布的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!