Block在NSDictionary(ARC)中发布 [英] Block gets released whilst in NSDictionary (ARC)

查看:145
本文介绍了Block在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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆