递归块过早地被释放

时间:2013-08-05 15:19:10

标签: ios objective-c objective-c-blocks

我按照these指南写了一个递归块:

NSMutableArray *groups = [NSMutableArray arrayWithArray:@[@"group1", @"group2", @"group3", @"group4"];

__block CommunicationCompletionHandler completion = [^{
    [groups removeObjectAtIndex:0];

    if ([groups count] > 0) {
        // This will send some information to the network, and calls the completion handler when it receives a response
        [mySocket saveGroup:groups[0] completion:completion];
    }
} copy]; // Removing copy here doesn't work either

[mySocket saveGroup:groups[0] completion:completion];

saveGroup:completion:方法中,我将完成处理程序添加到数组中:

self.completionHandlers[SaveGroupCompletionHandlerKey] = [completion copy];

当我收到回复时,我会调用以下方法(key就是SaveGroupCompletionHandlerKey}:

- (void)performCompletionHandlerForKey:(NSString *)key {
    if (self.completionHandlers[key]) {
        ((CommunicationCompletionHandler)self.completionHandlers[key])();
        [self.completionHandlers removeObjectForKey:key];
    }
}

问题是完成处理程序只被调用一次。 removeObjectForKey:行使块解除分配。如果我取消注释该行,一切正常。我不确定该数组是如何最后引用此块的,因为我添加了copy(我相信它正在优化为retain)。

为清楚起见,应用程序的流程为:

  • 通过网络发送第一组数据
  • 收到回复
  • 致电完成处理程序
  • 在完成处理程序中,发送下一组的数据(这是递归部分)。

这里有谁可以指出我做错了什么?

2 个答案:

答案 0 :(得分:3)

-performCompletionHandlerForKey:中,在执行块之后从字典中删除完成处理程序,这意味着在一次运行后将始终从字典中删除处理程序。

相反,将块存储在临时变量中,并在执行块之前将其从字典中删除。

顺便说一下,删除弱引用的建议是错误的。在您的代码现在编写时,您的块将永远不会被释放。典型的块递归模式是:

__weak __block MyBlock weakHandler;
MyBlock handler = ^ { 
    if (foo) {
        MyBlock strongHandler = weakHandler;
        [bar asyncOperationWithCompletion:strongHandler];
    }
};

weakHandler = handler;
[bar asyncOperationWithCompletion:handler];

答案 1 :(得分:-1)

避免保留保留周期的一种流行方法是在定义块之前创建对象的弱引用,然后在块内创建一个强引用并将其设置为该弱引用。此方法经常用于避免强烈捕获块内的self

- (void)someMethod {
    __weak MyType *weakSelf = self;
    [self someMethodWithABlockArg:^{
        MyType *strongSelf = weakSelf;
        [strongSelf someOtherMethod];
    }];
}

块内创建的强引用可防止在块运行时释放对象。当然,您可以对任何对象类型执行相同的操作。

Edit2:看起来[someBlock copy]确实很好。你试过在代码上运行Analyze吗?可能是completion在被引用到块内部时尚未初始化。