Consider the following C++ method:
class Worker{
....
private Node *node
};
void Worker::Work()
{
NSBlockOperation *op=[NSBlockOperation blockOperationWithBlock: ^{
Tool hammer(node);
hammer.Use();
}];
....
}
What, exactly, does the block capture when it captures "node"? The language specification for blocks, http://clang.llvm.org/docs/BlockLanguageSpec.html, is clear for other cases:
Variables used within the scope of the compound statement are bound to the Block in the normal manner with the exception of those in automatic (stack) storage. Thus one may access functions and global variables as one would expect, as well as static local variables. [testme]
Local automatic (stack) variables referenced within the compound statement of a Block are imported and captured by the Block as const copies.
But here, do we capture the current value of this? A copy of this using Worker’s copy constructor? Or a reference to the place where node is stored?
In particular, suppose we say
{
Worker fred(someNode);
fred.Work();
}
The object fred may not exist any more when the block gets run. What is the value of node? (Assume that the underlying Node objects live forever, but Workers come and go.)
If instead we wrote
void Worker::Work()
{
Node *myNode=node;
NSBlockOperation *op=[NSBlockOperation blockOperationWithBlock: ^{
Tool hammer(myNode);
hammer.Use();
}];
....
}
is the outcome different?
An Objective-C class defines an object that combines data with related behavior. Sometimes, it makes sense just to represent a single task or unit of behavior, rather than a collection of methods.
Objective-C is known as an Object Oriented Programming (OOP) language. OOP is a way of constructing software application composed of objects.
The goal of the @property directive is to configure how an object can be exposed. If you intend to use a variable inside the class and do not need to expose it to outside classes, then you do not need to define a property for it. Properties are basically the accessor methods.
In C++, when you write an instance variable node
, without explicitly writing something->node
, it is implicitly this->node
. (Similar to how in Objective-C, if you write an instance variable node
, without explicitly writing something->node
, it is implicitly self->node
.)
So the variable which is being used is this
, and it is this
that is captured. (Technically this
is described in the standard as a separate expression type of its own, not a variable; but for all intents and purposes it acts as an implicit local variable of type Worker *const
.) As with all non-__block
variables, capturing it makes a const
copy of this
.
Blocks have memory management semantics when they capture a variable of Objective-C object pointer type. However, this
does not have Objective-C object pointer type, so nothing is done with it in terms of memory management. (There is nothing that can be done in terms of C++ memory management anyway.) So yes, the C++ object pointed to by this
could be invalid by the time the block runs.
According to this page:
In general you can use C++ objects within a block. Within a member function, references to member variables and functions are via an implicitly imported
this
pointer and thus appear mutable. There are two considerations that apply if a block is copied:
- If you have a __block storage class for what would have been a stack-based C++ object, then the usual copy constructor is used.
- If you use any other C++ stack-based object from within a block, it must have a const copy constructor. The C++ object is then copied using that constructor.
Empirically, I observe that it const copies the this
pointer into the block. If the C++ instance pointed to by this
is no longer at that address when the block executes (for instance, if the Worker instance on which Worker::Work()
is called was stack-allocated on a higher frame), then you will get an EXC_BAD_ACCESS or worse (i.e. pointer aliasing). So it appears that:
this
, not copying instance variables by value.this
alive.Alternately, if I reference a locally stack-allocated (i.e. declared in this stack frame/scope) C++ object, I observe that its copy constructor gets called when it is initially captured by the block, and then again whenever the block is copied (for instance, by the operation queue when you enqueue the operation.)
To address your questions specifically:
But here, do we capture the current value of
this
? A copy of this using Worker’s copy constructor? Or a reference to the place where node is stored?
We capture this
. Consider it a const-copy of an intptr_t
if that helps.
The object
fred
may not exist any more when the block gets run. What is the value ofnode
? (Assume that the underlying Node objects live forever, but Workers come and go.)
In this case, this
has been captured by-value and node
is effectively a pointer with the value this + <offset of node in Worker>
but since the Worker instance is gone, it's effectively a garbage pointer.
I would infer no magic or other behavior other than exactly what's described in those docs.
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