Objective-C my C++ – Blocks

The Objective-C side

A really handy Objective-C feature that came to my attention not so long ago are blocks. Now I’m not going to write much about blocks themselves, for that there are much better docs. But here’s the basic idea:
Blocks are anonymous functions that retain the environment where they are created and abstract functionality. Depending on how that environment is presented, the block can modify it or not. Simple, right? Not exactly, but lets see an example:


__block int variable = 1;
int anotherVariable = 2;
void (block^)() = ^{
variable++; // actually modifying variable
anotherVariable++; // modifying a copy
}

view raw

gistfile1.m

hosted with ❤ by GitHub

A key word from before is “retain”. Objective-C uses retain count (or use count, if you are more C++) and that concept is embeded in blocks. That’s something we should keep in mind.

C++0x lambdas

C++’s latest additions gives us lambdas, which are the closest concept to blocks so far. The main difference resides in how it handles its environment.  As with everything in C++, you don’t have too many restrictions and you control a lot, and in this case you control how your lambda handles its environment. Lets see a simple example:


int nonModifiable = 42;
int modifiable = 23;
auto myLambda = [=, &modifiable]() mutable { modifiable++; nonModifiable++; };
myLambda(); // modifiable == 24, nonModifiable == 42

view raw

gistfile1.cpp

hosted with ❤ by GitHub

As a quick reference, the extract [=, &modifiable] means “treat modifiable as a reference and capture as a copy everything else”. Which is equivalent in this tiny example to [nonModifiable, &modifiable].
Everything is beautiful, but with a lot of flexibility comes a lot of things to think about so the building doesn’t fall to pieces after a few loops.

Limitations of lambdas

Lambdas don’t have limitations per-sé, but depending on what you want to use them for, there may be a couple of problems with them. So, where can we use lambdas? Completion handlers, error handlers, event handlers. If you want to do really useful things with these, you will probably want to call some method of an object for example, but who handles the life of that object? Is it just a pointer? A reference? Probably not a copy. There’s an easy way of handling this though.

Final thoughts

I’m trying to embrace the new standard as much as possible lately, and the new features come to the rescue, as usual. This time it’s called std::shared_ptr.
The basic idea is to capture by copy everything that you need. Use mostly shared_ptrs, and everything will be ok.
So why am I so worried about the lifetime of objects? With every type of deferred call, it’s something you always have to have in mind, but with game programming particularly, when one of your objects might literally die because it’s may be a bomb that has just went off, or an enemy spaceship that you just eliminated, you really need to be careful. The point in this kind of scenarios is probably check that the object the shared_ptr is wrapping is in use by something else, otherwise what’s the point of modifying something only the lambda has ownership for?
And here’s some code with the help of Cocos2d-X:


std::shared_ptr<GameObject> gameObject { std::make_shared<GameObject>("Bomb") };
cocos2d::CCLabelTTF *myLabel = cocos2d::CCLabelTTF::create("Explode!", "Arial", 30.0f);
auto myLambda = [gameObject]()
{
if (gameObject.use_count() > 1)
{
gameObject->explode();
}
});
cocos2d::CCMenuItemLabel *myItem = cocos2d::CCMenuItemLabel::createWithBlock(myLabel, myLambda);

view raw

gistfile1.cpp

hosted with ❤ by GitHub

Once the lambda is created, the use_count() for the gameObject is increased in 1, and as long as the lambda exists the gameObject will be retained. So this toy scenario might have a bug, where gameObject is never released completely. So this should be handled carefully, but we can just remove the menu after the gameObject explodes. In general, Cocos2d-X is not prepared for this kind of uses yet, especially because they do their own reference counting in an explicit way (not like shared_ptr).

References

[0] Cocos2d-X http://cocos2d-x.org/
[1] Mike Ash on blocks vs lambdas http://www.mikeash.com/pyblog/friday-qa-2011-06-03-objective-c-blocks-vs-c0x-lambdas-fight.html
[2] Apple documentation for blocks http://developer.apple.com/library/ios/#featuredarticles/Short_Practical_Guide_Blocks/_index.html
[3] std::shared_ptr http://en.cppreference.com/w/cpp/memory/shared_ptr

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s