Consider this simple example
int i = 42;
int (^aBlock)() = ^ {
return i;
};
NSLog(@"Class: %@", [aBlock class]);
Without ARC the above code prints
Class: __NSStackBlock__
whereas with ARC it prints
Class: __NSMallocBlock__
I placed a symbolic breakpoint on _Block_copy
and it looks like ARC is inserting a Block_Copy()
call, causing the block to be moved to the heap
It seems an unnecessary overhead and it defeats the whole purpose of having blocks on the stack in the first place.
Is this a limitation of ARC or is it a design choice?
Block pointers types are considered retainable object pointers types by ARC and such types - in absence of explicit ownership qualifier - are implicitly assumed to have a __strong qualifiers
, as per the documentation:
If an object is declared with retainable object owner type, but without an explicit ownership qualifier, its type is implicitly adjusted to have __strong qualification.
So the above example is equivalent to
int i = 42;
__strong int (^aBlock)() = ^ {
return i;
};
NSLog(@"Class: %@", [aBlock class]);
The documentation also states:
For
__strong
objects, the new pointee is first retained; second, the lvalue is loaded with primitive semantics; third, the new pointee is stored into the lvalue with primitive semantics; and finally, the old pointee is released.
and later
[...] whenever these semantics call for retaining a value of block-pointer type, it has the effect of a Block_copy [...]
So yes, ARC introduces a Block_copy
call whenever assigning a block to a variable, since the variable is implicitly assumed considered to be __strong
-qualified.
Skipping the assignment will keep the block on the stack. Consider the following example:
NSLog(@"Class: %@", [^int{return i;} class]); // => Class: __NSStackBlock__
The documentation also tells us that
The optimizer may remove such copies when it sees that the result is used only as an argument to a call.
And indeed it does. As proposed by Catfish_Man, turning the optimizations on (in this case by using the Release build configuration) will strip away the Block_Copy
call, leaving the block on the stack.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With