I've just stumbled over the following SO topic: Why should we copy blocks rather than retain? which has the following sentence:
However, as of iOS 6 they are treated as regular objects so you don't need to worry.
I was really confused by this assertion that is why I am asking: does this assertion really imply that Objective-C developers do not need to
@property (copy) blockProperties
or
[^(...){...) {} copy]
to copy blocks and their contents from stack to heap anymore?
I hope the description I've made is clear.
Please, be verbose.
Similar questions
Under ARC, are Blocks automatically copied when assigned to an ivar directly?.
ARC will copy the block automatically. From clang's Objective-C Automatic Reference Counting documentation:
So blocks used only as arguments to function or method calls may remain stack blocks, but otherwise anywhere that ARC retains the block it will copy the block. This is implemented by the compiler emitting a call to
objc_retainBlock()
, the implementation for which is:It is still a good idea to declare block properties as having copy semantics since a block assigned to a strong property will in fact be copied. Apple recommends this as well:
Note that since this copy-on-retain feature is provided by ARC it is dependent only on ARC or ARCLite availability and does not otherwise require a particular OS version or
OS_OBJECT_USE_OBJC
.Edit:
It turned out the examining the addresses of the "captured" variables are difficult to interpret, and not always appropriate to figure out whether a Block has been copied to the heap or still resides on the heap. Although the Specification of Blocks given here BLOCK IMPLEMENTATION SPECIFICATION, will sufficiently describe the facts, I try a completely different approach:
What is a Block anyway?
This is a summary of the official Specification BLOCK IMPLEMENTATION SPECIFICATION:
A Block exists of code (like a function) and a structure containing several peaces of data, flags and function pointers AND a variable length section for "captured variables".
Note that this structure is private and implementation defined.
A Block may be defined in function scope, where this structure is created in stack local memory, or it may be defined in global or static scope, where the structure is created in static storage.
A Block may "import" other Block references, other variables and
__block
modified variables.When a Block references other variables, they will be imported:
A stack local (automatic) variable, will be "imported" by means of making a "const copy".
A
__block
modified variable will be imported by means of assigning a pointer the address of that variable enclosed in another structure.Global variables will be simply referenced (not "imported").
If a variable will be imported, the "captured variable" lives in the aforementioned structure in the variable length section. That is, the "counterpart" of the automatic variable (which lives outside the block) has a storage within the block's structure.
Since this "captured variable" is read only, the compiler can apply a few optimizations: for example we really only need one instance of the captured variable on the heap, if we ever need a copy of the block.
The value of captured variables will be set when the block literal expression will be evaluated. This also implies, that the storage of the captured variables must be writable (that is, no code section).
The lifetime of captured variables is that of a function: each invocation of that block will require a new copy of these variables.
Captured variables in Blocks which live on the stack are destroyed when the program leaves the compound statement of the block.
Captured variables in Blocks which live on the heap are destroyed when the block will be destroyed.
According the Block API, versions 3.5:
Initially, when a block literal is created, this structure will exist on the stack:
Please note that this is for Block literals.
According the Objective-C Extensions to Blocks, the compiler will treat Blocks like objects.
Observations
Now, it's difficult to craft test code which proves these assertions. Thus, it seems better to use the debugger and set symbolic breakpoints at relevant functions
_Block_copy_internal
andmalloc
(which should be enabled only after the first breakpoint has been hit)and then run suitable test code (like the snippets below) and examine what happens:
In the following code snippet, we create a Block literal and pass it through as parameter to a function which calls it:
The output is as follows:
The address of the "captured" variable
x0
strongly indicates that it lives on the stack.We also have set a breakpoint at
_Block_copy_internal
- however, it won't be hit. This indicates, that the Block has not been copied onto the heap. Another proof can be made with Instruments, which doesn't show allocations in functionfoo
.Now, if we create and initialize a block variable, it seems, the Block data structure of the original Block literal - which is initially created on the stack, will be copied onto the heap:
This above copies the block which has been initially created on the stack. This may simply happen due to ARC and the fact, that we have a block literal on the right hand, and the block variable
block
on the left hand will be assigned the block literal - which results in aBlock_copy
operation. This makes Blocks appear much like normal Objective-C objects.The Allocations traced with Instruments in this similar code below
show, that the Block will be indeed copied:
Noteworthy
When a block doesn't capture any variables, it is like a normal function. Then it makes sense to apply an optimization, where a
Block_copy
operation does actually nothing, since there is nothing to copy. clang implements this with making such blocks a "global block".When sending
copy
to a block variable and assigning the result to another block variable, e.g.:copy
will be a quite cheap operation, since the blockblock
has already allocated storage for the block's structure which can be shared. Thus,copy
doesn't need to allocate storage again.Back to the question
To answer the question if we need to explicitly copy a block in certain circumstances, for example:
Well, once the block has been copied, no matter when, and will then be assigned the target variable (a Block variable) - we should be always safe without explicitly copying the block, since it is already in the heap.
In case of the block property, it should be safe if we would simply wright:
and then:
This should be safe since assigning a block literal (which lives on the stack) the underlying block variable
_completion
, will copy the block onto the heap.Nonetheless, I would still recommend to use attribute
copy
- since it is still suggested by the official documentation as best practice, and also supports older APIs where Blocks don't behave like normal Objective-C objects and where there is no ARC.Existing system APIs will also take care of copying a Block if required:
dispatch_async() will make the copy for us. So, we don't have to worry.
Other scenarios are more subtle:
But actually, this is safe: The block literal will be copied and assigned the block variable
block
.This example might look even scary:
But it seems, the Block literal will be correctly copied as an effect of assigning the argument (the Block) to the parameter (an
id
) in methodarrayWithObject:
which copies the Block literal onto the heap:Additionally,
NSArray
will retain the object passed as parameter in methodarrayWithObject:
. This causes another call toBlock_copy()
however, since the Block is already onto the heap, this call does not allocate the storage.This indicates, that a "retain" message sent to a Block literal somewhere in the implementation of
arrayWithObject:
would also indeed copy the Block.So, when do we actually need to explicitly copy a Block?
A block literal - e.g. the expression:
will create the block structure on the stack.
So, we actually need to make a copy only in cases where we need the Block on the heap and when this doesn't happen "automatically". However, there are virtually no scenarios where this is the case. Even in cases where we pass a Block as a parameter to a method which has no idea that it is a Block literal and requires a
copy
first (e.g.:arrayWithObject:
), and possibly the receiver sends just a retain message to the object given in the parameter, the Block will be copied onto the heap first.Examples where we might explicitly use
copy
, is where we do not assign a Block literal a block variable or anid
, and thus ARC cannot figure out that it has to make copy of the block object or has to send "retain".In this case, the expression
will yield an autoreleased Block whose structure resides on the heap.
Mine original answer was wrong. Edited answer is not an answer, but more of a "it's indeed a good question" thing.
Please, see Matt's answer for real references why stuff is the way it is.
After testing the following code on iOS7:
I got this:
Which pretty much means that blocks created and assigned into stack variables are actually already on a heap and copying them doesn't affect anything.
This, though, is not backed up by any official source, as they still state that blocks are created on stack on need to be copied "when passing down".
Part of the answer before I tested, stating which docs are contradicted by the example.
Document about transition to ARC states:
And docs about blocks and properties says: