Posts Tagged ‘Bug’

Blocks not capturing “by reference” variables

Monday, May 21st, 2012

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!