Does ARC handle this case without leakage? If so, how?

ARC seems very nice, but there are one or two edge cases that the typical naming conventions / rules don't let me understand. Take a look at the following category implementation around NSThread:

@interface NSThread (BlockAdditions) - (void)performBlock:(dispatch_block_t)block; @end @implementation NSThread (BlockAdditions) - (void)internal_performBlock:(dispatch_block_t)block { block(); } - (void)performBlock:(dispatch_block_t)block { [self performSelector:@selector(internal_performBlock:) onThread:self withObject:[block copy] waitUntilDone:NO]; } 

My question is: block leak after calling -copy ? How does the compiler know when to release a block? The tools do not detect a leak, but this does not convince me, given that I know about ARC that this case is handled correctly. Thanks for any info!

+4
source share
2 answers

This will be a leak in hold / release, but should not leak into ARC.

The compiler sees -copy , and that means -release is required. If you look at the generated assembly, that should be exactly what you see.

(well, exactly what you see when you get through the assembly, which is not so simple).

Note that you can simplify the assembly by simply compiling [block copy]; .

+5
source

The compiler sees that block copied in the performBlock: method. This creates a new object in the method, and each object created in the method must be freed before the method returns, if this object is not returned, in which case it must be auto-implemented, since after the method returns the compiler does not refer to the variables in the method anymore and therefore will never be able to free him.

So your method is roughly translated into

 - (void)performBlock:(dispatch_block_t)block { dispatch_block_t blockCopy; blockCopy = [block copy]; [self performSelector:@selector(internal_performBlock:) onThread:self withObject:blockCopy waitUntilDone:NO]; // If method returns, how shall the compiler access blockCopy any longer? // It can't! And since blockCopy is not returned by the method, it must // be destroyed before the method returns. [blockCopy release]; } 

You may wonder why this code does not crash then, will blockCopy not be freed? No, because performSelector:onThread:withObject:waitUntilDone: saves the object that you pass to it as an argument withObject: until it executes the selector callback, after that it will free the object again. Therefore, when release is called at the end of performBlock: blockCopy has a retainCount of 2, and this release reduces it to 1, but not to 0, so it is not freed. Only after your selector has been called in another thread, blockCopy will be released again, and since your called selector did not save it, it will finally be canceled.

+1
source

All Articles