Examine the following code, and assume it was compiled under ARC:
- (void)foo { NSOperationQueue *oq = [[NSOperationQueue alloc] init]; [oq addOperationWithBlock:^{ // Pretend that we have a long-running operation here. }]; }
Although the operation queue is declared as a local variable, its lifetime continues beyond the scope of the method as long as it has running operations.
How is this achieved?
UPDATE:
I appreciate Rob Mayoff's well-thought-out comments, but I think I did not ask my question correctly. I am not asking a specific question about NSOperationQueue, but rather a general question about object lifetime in ARC. Specifically, my question is this:
How, under ARC, can an object participate in the management of its own lifetime?
I've been a programmer for a very long time, and I'm well aware of the pitfalls of such a thing. I am not looking to be lectured as to whether this is a good or bad idea. I think in general it is a bad one. Rather, my question is academic: Whether it's a good or bad idea or not, how would one do this in ARC and what is the specific syntax to do so?
As a general case you can keep a reference to yourself. E.g.:
In the example code give, under ARC, the NSOperationQueue, being local to the enclosing lexical scope of the block, is captured is captured by the block. Basically, the block saves the value of the pointer so it can be accessed from within the block later. This actually happens regardless of whether you're using ARC or not; the difference is that under ARC, object variables are automatically retained and released as the block is copied and released.
The section "Object and Block Variables" in the Blocks Programming Topics guide is a good reference for this stuff.
Here are a few possibilities:
The
NSOperationQueue
retains itself until it is empty, then releases itself.The
NSOperationQueue
causes some other object to retain it. For example, sinceNSOperationQueue
uses GCD, perhapsaddOperationWithBlock:
looks something like this:In that code, the
wrapperBlock
contains a strong reference to theNSOperationQueue
, so (assuming ARC), it retains theNSOperationQueue
. (The realaddOperationWithBlock:
is more complex than this, because it is thread-safe and supports executing multiple blocks concurrently.)The
NSOperationQueue
doesn't live past the scope of yourfoo
method. Maybe by the timeaddOperationWithBlock:
returns, your long-running block has already been submitted to a GCD queue. Since you don't keep a strong reference tooq
, there is no reason whyoq
shouldn't be deallocated.The simplest thing I can think of would be to have a global NSMutableArray (or set, or whatever) that the object adds itself to and removes itself from. Another idea would be to put the (as you've already admitted) oddly-memory-managed code in a category in a non-ARC file and just use -retain and -release directly.