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];
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.
source share