"Capturing 'self' strongly in this block is likely to lead to a retain cycle"

  • I get the mentioned warning in my code and I'm wondering if this a possibly issue in my case:

    ARC enabled.
    The warning "Capturing 'self' strongly in this block is likely to lead to a retain cycle" is issued in this method:

    - (void) foo
    {
        [self.operationQueue addOperationWithBlock:^{
            [self bar];
        }];
    }

    property operationQueue is declared 'strong'.

    I understand the message, that is:

    'self' is strongly retaining _operationQueue which itself strongly retains a block which itself strongly retains 'self'.

    However, is this a problem in this case? I expect message bar to be queued in the operation queue. Meanwhile all references to 'self' diminish, except the one in the block. The block executes and eventually finishes and releases 'self' - possibly the last reference.

    Additionally, in this case, it is required that 'self' must not be destroyed prematurely - that is, before message bar finished.

    Doesn't break the cycle automatically - and isn't this warning over-alert?

    Thanks for clarification!
  • On 9 Jul 2012, at 10:40 AM, Andreas Grosam wrote:

    > The warning "Capturing 'self' strongly in this block is likely to lead to a retain cycle" is issued in this method:
    >
    > - (void) foo
    > {
    > [self.operationQueue addOperationWithBlock:^{
    > [self bar];
    > }];
    > }
    >
    > property operationQueue is declared 'strong'.
    >
    > I understand the message, that is:
    >
    > 'self' is strongly retaining _operationQueue which itself strongly retains a block which itself strongly retains 'self'.

    No. _operationQueue has nothing to do with it. The capture referred to has only to do with the block. As the warning says, it's a capture of self.

    > However, is this a problem in this case? I expect message bar to be queued in the operation queue. Meanwhile all references to 'self' diminish, except the one in the block. The block executes and eventually finishes and releases 'self' - possibly the last reference.

    You correctly describe the cycle. In practice, NSOperationQueue probably releases the block when it's done with it, and breaks the cycle, but clang can't know that, so it has to warn of the "likely" cycle.

    You can break this by having a strong reference to self that the block can manage independently.

      __block MyClass *      blockSelf = self;
      [self.operationQueue addOperationWithBlock:^{
          [blockSelf bar];
          blockSelf = nil;
      }];

    — F
  • On Mon, 9 Jul 2012 11:03:50 -0500, Fritz Anderson said:

    > You correctly describe the cycle. In practice, NSOperationQueue probably
    > releases the block when it's done with it, and breaks the cycle, but
    > clang can't know that, so it has to warn of the "likely" cycle.
    >
    > You can break this by having a strong reference to self that the block
    > can manage independently.
    >
    > __block MyClass *      blockSelf = self;
    > [self.operationQueue addOperationWithBlock:^{
    > [blockSelf bar];
    > blockSelf = nil;
    > }];

    A contortion I guess those of us switching from GC to ARC will have to sprinkle around. :(  Shame.

    --
    ____________________________________________________________
    Sean McBride, B. Eng                <sean...>
    Rogue Research                        www.rogue-research.com
    Mac Software Developer              Montréal, Québec, Canada
  • On 10/07/2012, at 2:03 AM, Fritz Anderson wrote:

    > In practice, NSOperationQueue probably releases the block when it's done with it

    I'm curious about your use of the word "probably" here. Can you explain?

    --
    Shane Stanley <sstanley...>
    'AppleScriptObjC Explored' <www.macosxautomation.com/applescript/apps/>
  • On 09.07.2012, at 18:03, Fritz Anderson wrote:

    > You can break this by having a strong reference to self that the block can manage independently.
    >
    > __block MyClass *      blockSelf = self;
    > [self.operationQueue addOperationWithBlock:^{
    > [blockSelf bar];
    > blockSelf = nil;
    > }];

    Thank you Fritz for your answer.

    But if I break the cycle and if I understand it correctly, 'self' won't be retained anymore within the block. If there are no more references to 'self' (except one possibly within the block), I fear 'self' will be destroyed before the block executes. I do require, though, that 'self' will be valid as long as the block is not finished.

    Note: I'm using ARC in which case a retainable pointer declared with __block will be retained (as opposed to manual ref counting), so I should use __weak or __unsafe_unretained instead of __block to break the cycle (if that is what I have to do).

    Andreas
  • >
    > On 09.07.2012, at 18:03, Fritz Anderson wrote:
    >
    >> You can break this by having a strong reference to self that the block
    >> can manage independently.
    >>
    >> __block MyClass *      blockSelf = self;
    >> [self.operationQueue addOperationWithBlock:^{
    >> [blockSelf bar];
    >> blockSelf = nil;
    >> }];
    >
    > Thank you Fritz for your answer.
    >
    > But if I break the cycle and if I understand it correctly, 'self' won't be
    > retained anymore within the block. If there are no more references to
    > 'self' (except one possibly within the block), I fear 'self' will be
    > destroyed before the block executes. I do require, though, that 'self'
    > will be valid as long as the block is not finished.
    >
    >
    >
    > Note: I'm using ARC in which case a retainable pointer declared with
    > __block will be retained (as opposed to manual ref counting), so I should
    > use __weak or __unsafe_unretained instead of __block to break the cycle
    > (if that is what I have to do).
    >
    > Andreas

    No, if you use the syntax Fritz suggests and there is no need to fear self
    being destroyed before the block is finished. This is also in the
    Transitioning To ARC release notes.

    blockSelf is retained by the block, since blockSelf just points to self,
    that retains self. At the end of the block, but not before, you're setting
    it to nil, which gets rid of that strong reference. All this little trick
    really does is 'rename' self to something else so you can manage its
    lifecycle.
  • On 9 Jul 2012, at 6:35 PM, Shane Stanley wrote:

    > On 10/07/2012, at 2:03 AM, Fritz Anderson wrote:
    >
    >> In practice, NSOperationQueue probably releases the block when it's done with it
    >
    > I'm curious about your use of the word "probably" here. Can you explain?

    The documentation for -addOperationWithBlock: does not _explicitly_ say that blocks are released after they are executed, so I didn't say so for a fact.

    But doing so is the only thing that makes sense, and conforms to the design pattern that containers retain (copy)/release the objects they contain. So the release is at least probable, if not certain.

    — F
previous month july 2012 next month
MTWTFSS
            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31          
Go to today