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.:
@implementation MasterOfMyOwnDestiny
{
MasterOfMyOwnDestiny *alsoMe;
}
- (void) lifeIsGood
{
alsoMe = self;
}
- (void) woeIsMe
{
alsoMe = nil;
}
...
@end
Here are a few possibilities:
The
NSOperationQueueretains itself until it is empty, then releases itself.The
NSOperationQueuecauses some other object to retain it. For example, sinceNSOperationQueueuses GCD, perhapsaddOperationWithBlock:looks something like this:- (void)addOperationWithBlock:(void (^)(void))block { void (^wrapperBlock)(void) = ^{ block(); [self executeNextBlock]; }; if (self.isCurrentlyExecuting) { [self.queuedBlocks addObject:wrapperBlock]; } else { self.isCurrentlyExecuting = YES; dispatch_async(self.dispatchQueue, wrapperBlock); } }In that code, the
wrapperBlockcontains 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
NSOperationQueuedoesn't live past the scope of yourfoomethod. 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 whyoqshouldn't be deallocated.
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.
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.
来源:https://stackoverflow.com/questions/10489995/manual-object-lifetime-with-arc