块在NSDictionary(ARC)中被释放



我正试图保留一个由方法传递给我的类的块的引用,以便稍后调用。然而,我在维护对它的引用时遇到了麻烦

我认为,显而易见的方法是将其添加到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中存储,则最好该死确保其中一个值不是nil

其中一个是。

ICYMI,它导致论点列表提前终止。我有一个userInfo类型的参数被传递到一个"添加到队列"方法中,当然,您可以传递"nil"。然后,当我构建字典时,丢弃那个参数会导致构造函数认为我已经终止了参数列表。@"callback"是字典构造函数中的最后一个值,它从未被存储。

与流行的错误概念相反,ARC不会自动取消作为参数传递给方法的块的堆叠。只有当从方法/函数返回块时,它才会自动取消堆栈。

也就是说。。。。

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

如果dict在作用域之外仍然存在,并且您尝试使用该块,则会崩溃(实际上,在这种情况下不会,因为这是一个静态块,但这是您不能依赖的编译器细节(。

这里记录了这一点:

区块如何在ARC中工作

当你在ARC模式下向上传递块时,块"就起作用",比如作为回报。您不再需要调用阻止复制你当将堆栈"向下"传递到CCD_ 11和其他做保留的方法

返回值行为可以自动化,因为返回基于堆的块总是正确的(返回基于堆栈的块总是错误的(。在块作为参数的情况下,不可能以一种既高效又始终正确的方式自动执行行为。

分析器可能应该对此使用发出警告。如果没有,请提交一个bug。

(当我指的是时,我删除了堆栈。对此感到抱歉。(


编译器没有自动将块作为参数,原因如下:

  • 不必要地将块复制到堆可能会对性能造成重大影响
  • 一个块的多个副本可能会大大增加性能损失

即:

 doSomethingSynchronous(aBlock);
 doSomethingSynchronous(aBlock);
 doSomethingSynchronous(aBlock);
 doSomethingSynchronous(aBlock);

如果这意味着有四个Block_copy((操作,并且aBlock包含大量捕获的状态,那么这将是一个巨大的潜在打击。

•一天中只有这么多小时,参数的自动化处理充斥着不明显的边缘情况。如果将来自动处理,就可以在不破坏现有代码的情况下完成,因此,也许将来也会这样做。

即编译器可以生成:

 aBlock = [aBlock copy];
 doSomethingSynchronous(aBlock);
 doSomethingSynchronous(aBlock);
 doSomethingSynchronous(aBlock);
 doSomethingSynchronous(aBlock);
 [aBlock release];

这不仅可以解决块作为参数的问题,而且在所有潜在用途中只会生成块的一个副本。


然而,问题仍然存在:为什么会发生这种情况?为什么不NSDictionary存储块?这与事实有关吗我的目标是iOS 4.3,因此ARC必须作为静态图书馆

那么,奇怪的事情正在发生。巧合的是,上周我在一个基于ARC的应用程序中使用了块作为值,它运行得很好。

你手边有一个最小的例子吗?

最新更新