在NSDictionary(ARC)中块被释放

时间:2011-11-11 05:01:38

标签: objective-c ios objective-c-blocks automatic-ref-counting

我正在尝试保留对通过方法传递给我的类的块的引用,以便稍后调用。但是,我很难保持对它的引用。

我认为,显而易见的方法是将其添加到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是字典构造函数中的最后一个值,它从未被存储过。

1 个答案:

答案 0 :(得分:31)

与流行的错误概念相反,ARC 不会自动对作为方法的参数传递的块进行反堆叠。它仅在从方法/函数返回块时自动解除堆栈。

即。此....

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

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

这是documented here

  

块如何在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的问题,而且还会在所有潜在用途中生成该块的一个副本。


  

但问题仍然存在:为什么会发生这种情况?为什么不会   NSDictionary存储一个块?它与事实有关吗?   我的目标是iOS 4.3,因此ARC必须作为静态内置   库中?

那时候有些奇怪的事情发生了。巧合的是,我上周在基于ARC的应用程序中使用了块值作为值,并且工作正常。

你有一个方便的小例子吗?

相关问题