0

I have a block to use as a completionHandler for an NSURLConnection asynchronous request whose main job is to spawn a new asynchronous request using the same block for the new requests completion handler. I am doing this because it effectively solves another problem which is to line up a sequence of asynchronous calls and have them fired off in the background. This is working marvelously for us, but we have a warning I am concerned about. Namely, XCode thinks I have a retain cycle. Perhaps I do, I don't know. I've been trying to learn about blocks over the last couple hours but I haven't found an explanation for recursive uses like mine. The warning states `Block will be retained by the captured object'.

My best guess so far is that a retain cycle is exactly what we want, and that to clear when we are done, we just nillify the block variable, which I'm doing. It doesn't get rid of the error, but I don't mind as long as I'm not leaking memory or doing some black magic I'm not aware of. Can anyone address this? Am I handling it right? If not, what should I be doing?

void (^ __block handler)(NSURLResponse *, NSData *, NSError*);
handler = ^(NSURLResponse *response, NSData *data, NSError *error)
{
    [dataArray addObject:data];

    if (++currentRequestIndex < [requestsArray count])
    {

        if (error)
        {
            [delegate requestsProcessWithIdentifier:_identifier processStoppedOnRequestNumber:currentRequestIndex-1 withError:error];
            return;
        }

        [delegate requestsProcessWithIdentifier:_identifier completedRequestNumber:currentRequestIndex-1]; // completed previous request

        [NSURLConnection sendAsynchronousRequest:[requestsArray objectAtIndex:currentRequestIndex]
                                           queue:[NSOperationQueue mainQueue]
                               completionHandler:handler]; // HERE IS THE WARNING
    }
    else
    {
        [delegate requestsProcessWithIdentifier:_identifier completedWithData:dataArray];
        handler = nil;
    }
};
[NSURLConnection sendAsynchronousRequest:[requestsArray objectAtIndex:0]
                                   queue:[NSOperationQueue mainQueue]
                       completionHandler:handler];
jakev
  • 2,815
  • 3
  • 26
  • 39
  • 2
    You have a re-entrant block, which will cause a retain cycle (that never ends). Create a second __block __weak reference to that block, outside of it. – Richard J. Ross III May 24 '13 at 00:33
  • @RichardJ.RossIII Thanks Richard, but I'm afraid this did not help. My delegate methods are not fired, I'm not sure where the block disappears to but it doesn't seem to run. – jakev May 24 '13 at 01:13

2 Answers2

0

Try to store your handler block into an instance variable of your view controller (or whatever class you're in).

Assuming that you declare an instance variable named _hander:

{
  void (^_handler)(NSURLResponse *, NSData *, NSError*);
}

Change your code to:

__weak __typeof(&*self)weakSelf = self;
_handler = ^(NSURLResponse *response, NSData *data, NSError *error)
{
  [dataArray addObject:data];

  if (++currentRequestIndex < [requestsArray count])
  {

    if (error)
    {
      [delegate requestsProcessWithIdentifier:_identifier processStoppedOnRequestNumber:currentRequestIndex-1 withError:error];
      return;
    }

    [delegate requestsProcessWithIdentifier:_identifier completedRequestNumber:currentRequestIndex-1]; // completed previous request

    __strong __typeof(&*self)strongSelf = weakSelf;
    [NSURLConnection sendAsynchronousRequest:[requestsArray objectAtIndex:currentRequestIndex]
                                       queue:[NSOperationQueue mainQueue]
                           completionHandler:strongSelf->_handler];
  }
  else
  {
    [delegate requestsProcessWithIdentifier:_identifier completedWithData:dataArray];
  }
};

[NSURLConnection sendAsynchronousRequest:[requestsArray objectAtIndex:0]
                                   queue:[NSOperationQueue mainQueue]
                       completionHandler:_handler];
Sylvain Guillopé
  • 1,358
  • 9
  • 21
0
void (^handler)(NSURLResponse *, NSData *, NSError*);
typeof(handler) __block __weak weakHandler;
weakHandler = handler = ^(NSURLResponse *response, NSData *data, NSError *error)
{
    [dataArray addObject:data];

    if (++currentRequestIndex < [requestsArray count])
    {

        if (error)
        {
            [delegate requestsProcessWithIdentifier:_identifier processStoppedOnRequestNumber:currentRequestIndex-1 withError:error];
            return;
        }

        [delegate requestsProcessWithIdentifier:_identifier completedRequestNumber:currentRequestIndex-1]; // completed previous request

        [NSURLConnection sendAsynchronousRequest:[requestsArray objectAtIndex:currentRequestIndex]
                                           queue:[NSOperationQueue mainQueue]
                               completionHandler:weakHandler]; // HERE IS THE WARNING
    }
    else
    {
        [delegate requestsProcessWithIdentifier:_identifier completedWithData:dataArray];
    }
};
newacct
  • 119,665
  • 29
  • 163
  • 224