I recently stumbled over a strange case when using blocks.
Please look at the following bit of Objective-C++ code:
typedef void (^BlockType)(); static BlockType blockTestByValue(const int value) { return [[^{NSLog(@"blockTestByValue &value = %p", &value);} copy] autorelease]; } static BlockType blockTestByReference(const int& value) { return [[^{NSLog(@"blockTestByReference &value = %p", &value);} copy] autorelease]; } void test() { BlockType direct; BlockType byValue; BlockType byReference; { int value = 42; NSLog(@"&value = %p", &value); direct = [[^{NSLog(@"direct &value = %p", &value);} copy] autorelease]; byValue = blockTestByValue(value); byReference = blockTestByReference(value); } direct(); byValue(); byReference(); }
Calling test() gives the following output:
&value = 0x7fff5fbff44c direct &value = 0x100c26a10 blockTestByValue &value = 0x100c26990 blockTestByReference &value = 0x7fff5fbff44c
You see, we have a stack based variable value
here that is used by blocks that are executed outside the living context of the variable value
. Therefore the value
variable gets “captured” by the block and a copy is made. This is true for all cases – except for the case where I pass the value
variable by reference to a function! I don’t see any reason why the variable should not be captured in this case. In my opinion the blockTestByReference
case should behave exactly like the direct
case, because in both cases value
is referencing exacly the same variable.
Any comments? Did I miss something in the specification of blocks, or did a discover a bug in the compiler?
Update:
I found the behavior described in the language specification:
For example, given class Foo with member function fighter(void): Foo foo; Foo &fooRef = foo; Foo *fooPtr = &foo; ...a Block that used foo would import the variables as const variations: const Foo block_foo = foo; // const copy constructor const Foo &block_fooRef = fooRef; Foo *const block_fooPtr = fooPtr;
So the compiler is really correct here. One must be very careful when using variables declared by reference in blocks!