I'm seeing some strange behavior when trying to capture an instance of a C++ class on the stack in an Objective-C block. Consider the following code:
#import <Foundation/Foundation.h>
#include <stdio.h>
struct Test
{
Test() : flag(0) { printf("%p default constructor\n", this); }
Test(const Test& other) : flag(0) { printf("%p copy constructor\n", this); }
~Test() { flag = 1; printf("%p destructor\n", this); }
int flag;
};
int main(int argc, char **argv)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
Test test;
void (^blk)(void) = ^(void)
{
printf("flag=%d (test=%p)\n", test.flag, &test);
};
printf("about to call blk\n");
blk();
[pool release];
return 0;
}
One would expect that when the local variable test
gets captured by the block blk
, it has a consistent state. However, that is not the case. The output of this code is the following:
0x7fff5fbff650 default constructor
0x7fff5fbff630 copy constructor
about to call blk
0x7fff5fbff5d0 copy constructor
0x7fff5fbff5d0 destructor
flag=1 (test=0x7fff5fbff5d0)
0x7fff5fbff630 destructor
0x7fff5fbff650 destructor
So the local Test
instance that the block sees has had its destructor called! Anything you do with it is undefined behavior, which would very likely lead to a crash (e.g. if the destructor deleted a pointer without setting it to NULL).
Are C++ class instance variables supported by Objective-C blocks? The Block Programming Topics section "C++ Objects" seems to indicate that they are, but they clearly aren't working here. Is this a bug in the compiler/runtime? This was tested on GCC 4.2.1, Apple build 5666, on Mac OS X v10.6.8.
__block is a storage qualifier that can be used in two ways: Marks that a variable lives in a storage that is shared between the lexical scope of the original variable and any blocks declared within that scope. And clang will generate a struct to represent this variable, and use this struct by reference(not by value).
Blocks are a language-level feature added to C, Objective-C and C++, which allow you to create distinct segments of code that can be passed around to methods or functions as if they were values. Blocks are Objective-C objects, which means they can be added to collections like NSArray or NSDictionary .
Objective-C is slightly slower than straight C function calls because of the lookups involved in its dynamic nature.
I'd say its a bug in GCC.
When I try it with GCC, I get:
0x7fff5fbff5d0 default constructor
0x7fff5fbff5c0 copy constructor
about to call blk
0x7fff5fbff570 copy constructor
0x7fff5fbff570 destructor
flag=1 (test=0x7fff5fbff570)
0x7fff5fbff5c0 destructor
0x7fff5fbff5d0 destructor
But using LLVM 2.0:
0x7fff5fbff610 default constructor
0x7fff5fbff600 copy constructor
about to call blk
flag=0 (test=0x7fff5fbff600)
0x7fff5fbff600 destructor
0x7fff5fbff610 destructor
The latter follows my interpretation of the documentation for blocks (and is the only version that isn't flagrantly broken).
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