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?
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:
It is capturing this, not copying instance variables by value.
Nothing is being done to keep the object pointed to by 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 of node? (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.
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.
Related
Could not find detailed description.
Is it an object or function?
Is it created in runtime on demand or at compile-time or at loading stage?
Where they are created: On heap or on stack?
What is the invocation procedure and order?
Is it an object or function?
Both; a block is effectively a captured bit of state that is also callable like a function. The compiler colludes to make the object part of it mostly transparent (especially with ARC).
Is it created in runtime on demand or at compile-time or at loading stage?
Runtime or compile-time, depending on the block.
A block that captures no state can be entirely created at compile-time. A block that captures state that can only be known at runtime will be created at runtime.
Where they are created: On heap or on stack?
Both; really, stack, heap, or static RO mapped memory (i.e. a compile time block).
Blocks (that capture state) are generally created on the stack and then promoted to the heap on the first Block_copy() operation.
What is the invocation procedure and order?
Unclear what you are asking here. Blocks are invoked just like C functions where the first argument is always a reference to the block object, giving a hook to grab the captured state.
I know each time I press CMD + B on my keyboard:
Xcode does wake up ARC
ARC analyzes my code and writes all the retain/release/autorelease invocations
finally the code is compiled by LLVM
A few more things happen in the process, however what I am asking is...
Where does ARC write the release instructions?
Just before the variable that reference an instance of a class goes out of scope
Just after the last time that variable is used
Example
class HugeObject {
func doVeryImportantStuff() { print("The answer is \((10*4)+2)") }
}
func foo() {
let a = HugeObject()
a.doVeryImportantStuff()
// <-- Point A
let b = HugeObject()
b.doVeryImportantStuff()
// <-- Point B
}
Where is ARC going to write the a.release() line?
At Point B?
Or, better, it is able to understand that the a.release can safely be moved at Point A?
I suspect there could be important implications in terms of footprint but I could not find any information about this.
First of all: ARC is a part of the compiler, whose name is clang, not LLWM. It is done while compiling.
To your Q:
Short answer: This depends on the annotations you gave the compiler. Default is not too early and not too late.
Long answer:
Semantically the release of a local var (local scope, auto) is sent, when the extent of the local var is lost. But this is optimized. Therefore it is possible that technically the release is sent earlier, that means between the last visible usage of the local var and the loss of the extent.
If you have a reason to keep the retain as long as the extent, you have to annotate objc_precise_lifetime.
In general, ARC maintains an invariant that a retainable object pointer held in a __strong object will be retained for the full formal lifetime of the object. Objects subject to this invariant have precise lifetime semantics.
By default, local variables of automatic storage duration do not have precise lifetime semantics. Such objects are simply strong references which hold values of retainable object pointer type, and these values are still fully subject to the optimizations on values under local control.
[…]
A local variable of retainable object owner type and automatic storage duration may be annotated with the objc_precise_lifetime attribute to indicate that it should be considered to be an object with precise lifetime semantics.
http://clang.llvm.org/docs/AutomaticReferenceCounting.html#precise-lifetime-semantics
Apple doc says "In a manually reference-counted environment, local variables used within the block are retained when the block is copied. Use of instance variables within the block will cause the object itself to be retained. "
I was going to check local variable is retained by block or not using retainCount, but failed.
Can anyone help me?
A block won't retain an object unless the block is copied. Since a block can only capture state within the same scope as that captured state, the implementation assumes no need to actually retain anything unless the block is copied for the purposes of escaping the scope of declaration.
Think of it in terms of "execution pointer" (kinda like when you are stepping through code in the debugger).
When the execution pointer passes over a block's declaration, that block captures a snapshot -- copies -- all variables that are used within the block's scope that are not declared within the block itself. For an object, that means the block makes a copy of the reference to the object, not a copy of the object itself.
A block starts on the stack. When a block is copied the first time, it is copied from the stack to the heap using a compiler generated per-block "copy helper" (a simple block may not have a copy helper and might actually never be on the stack). That copy helper will retain any objects referenced by the block (that are not referenced via an __block variable anyway).
They won't be released until the block is released and deallocated.
retainCount is useless.
trust the documentation for the implementation. if that fails or defies your expectation for some reason, provide a sample program.
there's no need to verify the retain count, because that's how it works.
if you do doubt it and want to sanity check it, Instruments can be configured to record reference count operations of NSObjects. in that case, run as usual (launched from Instruments, of course), locate the instances of interest in the list of allocated objects, then evaluate the backtraces of the ref-count-ops of the objects of interest. you should see it in there.
I was under the impression that blocks were supposed to resemble first-class functions and allow for lambda calc-style constructs. From a previous question however, I was told they are actually just objects.
Then I have 2 questions really:
Besides the feature of having access to their defining scope, which
I guess makes them usable in a way resembling C++ "friendship", why
would one go for a block instead of an object then? Are they more
lightweight? Because if not I might as well keep passing objects as
parameters instead of blocks.
Do blocks have a way of keeping an internal state? for instance,
some variable declared inside the block which will retain its value
across invocations.
Besides the feature of having access to their defining scope, which I guess makes them usable in a way resembling C++ "friendship", why would one go for a block instead of an object then?
Flexibility. Less to implement. A block is able to represent more than a parameter list or specific object type.
Are they more lightweight?
Not necessarily. Just consider them another tool in the toolbox, and use them where appropriate (or required).
Do blocks have a way of keeping an internal state? for instance, some variable declared inside the block which will retain its value across invocations.
Yes, they are able to perform reference counting as well as copy stack objects. That doesn't necessarily make them lighter-weight to use than an object representing the parameters you need.
Related
What's the difference between NSInvocation and block?
blocks were supposed to resemble first-class functions [...] they are actually just objects.
They are in fact first-class functions, implemented for the purposes of ObjC as objects. In plain-C, where they are also available, they have a closely-related but non-object-based implementation. You can think about them in whichever way is most convenient at the moment.
why would one go for a block instead of an object then?
A block is an executable chunk of code which automatically captures variables from its enclosing scope. The state and actions of a custom object have to be more explicitly handled, and are less generic; you can't use any old object as a completion argument, whereas an executable object fits that bill perfectly.
Do blocks have a way of keeping an internal state? for instance, some variable declared inside the block which will retain its value across invocations.
Sure, you can declare a static variable just like you could with a function or method:
void (^albatross)(void);
albatross = ^{
static int notoriety;
NSLog(#"%d", notoriety++);
};
albatross();
albatross();
albatross();
albatross();
when i say block i mean:
^(int a) {return a*a;};
besides, block is only support by iOS4 and above.
What is the difference between these two?
An NSInvocation is a message (using a selector) to an object, with optional parameters, which can be executed later (or now), and outside the current context (mind of course what you copy vs retain or reference if you move it). NSInvocation has the benefit that you can selectively copy/refer to exactly what you need.
The block is a secret local function definition, which is able to capture portions of the current thread's context, or altogether. It's also a little easier to configure than an NSInvocation because it automatically captures, copies, and retains the thread (or scope) local context. Blocks can increase your binary size slightly, similar to functions. If taken out of the local context (e.g. when you copy a block), blocks can require quite a bit more CPU time and memory - when compared to NSInvocation.
NSInvocation is an object that encapsulates a message call: the target object, the selector, the arguments and the return value. A block is an object that encapsulates a section of code and some information about the state of the program leading up to that section: specifically it records the variables on the call stack up to the creation of the block.
Both of these can clearly be used as callbacks: you can use an invocation to send a message to an object, or you can execute a block's code just like a function. What's different about them is the way you'd transport state in each case. With an invocation, you either need the target object or one of the parameters to represent the context in which the message is appearing. With a block, this context is captured automatically from the state when the block was created.
To put it very simply, NSInvocation is less powerful than blocks. It just encapsulates a single method call on a single object, whereas blocks can wrap many lines of arbitrary code. Even your very simple squaring block is impossible to represent using an invocation without support from an existing class that would do the squaring itself.