Using C++ safely in Blocks (and lambdas) for the Objective C programmer
Block syntax was introduced by Apple to clang in 2008 and has slowly been sprinkled throughout the operating system frameworks ever since. It pre-dated lambdas in C++ (which appeared in C++11) and is compatible with C, Objective C and Objective C++.
A lot of documentation has been written about using Blocks in Objective C, such as the infamous gosh-darn-block-syntax site, and the perils of retain loops in blocks. The “strong-self/weak-self” pattern is well established for Objective C programmers, but very little appears to have been written about using (Objective) C++ safely with blocks, which is unfortunate, because it is really easy to mess up.
Let’s take the “simple” Objective C example:
__weak __typeof__(self) weakSelf = self;
dispatch_group_async(_operationsGroup, _operationsQueue, ^
{
__typeof__(self) strongSelf = weakSelf;
[strongSelf doSomething];
});
How would we code that with C++ classes instead of Objective C classes. Something akin to:
__weak __typeof__(this) weakThis = this;
dispatch_group_async(_operationsGroup, _operationsQueue, ^
{
__typeof__(this) strongThis = this;
strongThis->DoSomething();
});
Note that the above code does compile and “run”(assuming you’ve defined the required bits and pieces), and only gives you a warning in Xcode 10.1:
‘__weak’ only applies to Objective-C object or block pointer types; type here is ‘typeof (this)’ (aka ‘MyClass *’)
This is unfortunate because ignoring the warning is very hazardous (may I suggest -Werror=ignored-attributes
to make this an error). The __weak
qualifier here is a complete no-op on the C++ this
pointer.
Officially “It is undefined behavior to perform an operation with weak assignment semantics with a pointer to an Objective-C object whose class does not support
__weak
references.”. I couldn’t find anything in the ARC documentation about__weak
on a pointer to a non-Objective-C object, so I guess that makes it undefined as well.
So semantically (ignoring the undefined behavior, and focusing on the observed behavior) the code above is equivalent to:
dispatch_group_async(_operationsGroup, _operationsQueue, ^
{
this->DoSomething();
});
which unfortunately compiles cleanly without any warnings/errors at all.
The good news about this code is that we can’t get a retain loop in C++ because there is no reference counting. The bad news is that if our C++ object gets disposed of before our block is finished, the this
pointer is going to be a dangling bad pointer.
It should be noted that there is a part of the Block specification devoted to C++ objects which says: To support member variable and function access the compiler will synthesize a const
pointer to a block version of the this
pointer. Which means that the block above can be simplified even more to:
dispatch_group_async(_operationsGroup, _operationsQueue, ^
{
DoSomething();
});
but it still contains a (arguably even more hidden due to the implicit this) dangling pointer problem. Also note that it is a const
pointer to this
as opposed to a const
pointer to a const
this
so the object referred to by this
is mutable.
So what we need is something semantically equivalent to the “strong-self/weak-self” pattern that we can use in C++.
C++ does have a “strong-self/weak-self” pattern using std::shared_ptr/std::weak_ptr which seems to fit the bill. What we might naively think we want is something like this:
std::weak_ptr<MyClass> weak_this(this);
dispatch_group_async(_operationsGroup, _operationsQueue, ^
{
auto strong_this = weak_this.lock(); strong_this->DoSomething();
});
Unfortunately there is no weak_ptr
constructor that takes a naked pointer, so this doesn’t compile at all. The only way to get a weak_ptr
is from a shared_ptr
. You may be tempted to do:
std::shared_ptr<MyClass> shared_this(this);
std::weak_ptr<MyClass> weak_this(shared_this);
dispatch_group_async(_operationsGroup, _operationsQueue, ^
{
auto strong_this = weak_this.lock(); strong_this->DoSomething();
});
which will compile just fine. Unfortunately you will find your this
pointer being deallocated when shared_this
goes out of scope which likely means it is going to be deallocated twice (once when the shared_ptr
goes out of scope, and once when whatever allocated this
decides to dispose of it), and very unlikely that weak_this
is ever going to give you a strong_this
because shared_this
is likely to go out of scope before the asynchronous block gets a chance to run.
Fundamentally this is a problem with std::shared_ptr
in that getting a std::shared_ptr
for this
is not possible for a this
that is already being managed by a std::shared_ptr
without having a reference to that managing std::shared_ptr
. Luckily the C++ standard has a solution for us, and that solution is std::enable_shared_from_this.
So that means we have to expand our example out a bit to catch all the relevant bits:
#include <memory>
#include <dispatch/dispatch.h>class MyClass : std::enable_shared_from_this<MyClass> {
public:
MyClass() {
std::weak_ptr<MyClass> weak_this(shared_from_this());
dispatch_async(dispatch_get_global_queue(
DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
{
auto strong_this = weak_this.lock();
strong_this->DoSomething();
});
}
private:
void DoSomething() { printf("Hi!\n"); }
};int main() {
MyClass a;
sleep(5); // To give the async op time to run
}
The code sample above will compile, but sadly it is absolutely riddled with errors.
When it is first run it will abort withterminating with uncaught exception of type std::__1::bad_weak_ptr: bad_weak_ptr
thrown from shared_from_this()
. Reading carefully through the documentation you will find that:
It is permitted to call
shared_from_this
only on a previously shared object, i.e. on an object managed by std::shared_ptr.
Alright, so we update our code to fix that (updated code in bold):
#include <memory>
#include <dispatch/dispatch.h>class MyClass : std::enable_shared_from_this<MyClass> {
public:
MyClass() {
std::weak_ptr<MyClass> weak_this(shared_from_this());
dispatch_async(dispatch_get_global_queue(
DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
{
auto strong_this = weak_this.lock();
strong_this->DoSomething();
});
}
private:
void DoSomething() { printf("Hi!\n"); }
};int main() {
std::shared_ptr<MyClass> a(new MyClass);
sleep(5); // To give the async op time to run
}
It compiles and runs and still throws an exception at the same place. Reading carefully through more documentation:
Publicly inheriting from
std::enable_shared_from_this<T>
provides the typeT
with a member functionshared_from_this
.
I added the emphasis on ‘publicly’. Would be really nice if the compiler enforced that little gotcha’. So we fix that up:
#include <memory>
#include <dispatch/dispatch.h>class MyClass : public std::enable_shared_from_this<MyClass> {
public:
MyClass() {
std::weak_ptr<MyClass> weak_this(shared_from_this());
dispatch_async(dispatch_get_global_queue(
DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
{
auto strong_this = weak_this.lock();
strong_this->DoSomething();
});
}
private:
void DoSomething() { printf("Hi!\n"); }
};int main() {
std::shared_ptr<MyClass> a(new MyClass);
sleep(5); // To give the async op time to run
}
It compiles, runs and still throws an exception at the same place. Reading carefully yet again:
…in particular,
shared_from_this
cannot be called in a constructor
since the std::shared_ptr
that is going to wrap this has not been constructed yet.
#include <memory>
#include <dispatch/dispatch.h>class MyClass : public std::enable_shared_from_this<MyClass> {
public:
void Register() {
std::weak_ptr<MyClass> weak_this(shared_from_this());
dispatch_async(dispatch_get_global_queue(
DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
{
auto strong_this = weak_this.lock();
strong_this->DoSomething();
});
}private:
void DoSomething() { printf("Hi!\n"); }
};int main() {
std::shared_ptr<MyClass> a(new MyClass);
a->Register();
sleep(5); // To give the async op time to run
}
which finally compiles and runs to completion. Note that to make our class safe for other users who don’t know about the shared_ptr
requirements, we should probably hide our constructor and give our users a factory method to work with that forces good behavior:
#include <memory>
#include <dispatch/dispatch.h>class MyClass : public std::enable_shared_from_this<MyClass> {
public:
static std::shared_ptr<MyClass> Create() {
return std::shared_ptr<MyClass>(new MyClass());
}
void Register() {
std::weak_ptr<MyClass> weak_this(shared_from_this());
dispatch_async(dispatch_get_global_queue(
DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
{
auto strong_this = weak_this.lock();
strong_this->DoSomething();
});
}private:
MyClass() {}
void DoSomething() { printf("Hi!\n"); }
};int main() {
auto a = MyClass::Create();
a->Register();
sleep(5); // To give the async op time to run
}
Hurrah! Finally a block safe call back (I think…).
Note that I used std::shared_ptr<MyClass>(new MyClass());
instead of std::make_shared<MyClass>();
. This is for a couple of reasons:
- Since I made my constructor private, I would have to go through a lot of convolutions to get the compiler to allow me to compile.
- Even if I get it compiling,
std::make_shared
is not always necessarily the best choice when working withstd::weak_ptrs
because of potential memory issues.
You may be tempted to think that you could avoid all of these convolutions with your own wrapper class. Something with a mutex that protected a naked pointer to this
that you would reset in your destructor. The versions that I have seen have wrapped the this
pointer in an Objective C class with a mutex. Probably because using Objective C kept the weak-self/strong-self paradigm that they were already familiar with. Something like:
#import <Foundation/Foundation.h>@class Wrapper;class MyClass {
public:
MyClass();
void DoSomething() { printf("Hi!\n"); }
private:
Wrapper *_wrapper;
};int main() {
MyClass a;
sleep(5); // To give the async op time to run
}@interface Wrapper : NSObject {
MyClass *_wrapped;
}
@end@implementation Wrapper
- (instancetype)initWithMyClass:(MyClass *)wrapped {
if ((self = [super init])) {
_wrapped = wrapped;
}
return self;
}
- (void)reset {
@synchronized(self) {
_wrapped = nullptr;
}
}
- (void)doSomething {
@synchronized(self) {
if (_wrapped) {
_wrapped->DoSomething();
}
}
}
@endMyClass::MyClass() {
_wrapper = [[Wrapper alloc] initWithMyClass:this];
__weak Wrapper *weakWrapper = _wrapper;
dispatch_async(dispatch_get_global_queue(
DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
{
Wrapper *strongWrapper = weakWrapper;
[strongWrapper doSomething];
});
}MyClass::~MyClass() {
[_wrapper reset];
}
Unfortunately you will always have a race condition between the time your object (MyClass) starts being destroyed, and when you can null out that naked pointer. If MyClass has a subclass it’s possible that it could be in a partially destroyed state if the callback comes between the time the destructor starts running and [_wrapper reset]
is called. As long as you guaranteed that MyClass
had no subclasses, and [_wrapper reset]
was the first thing you called in your destructor you may be alright here.
Also see my post on Objective C Encoding, and discover that by mixing C++/Objective C the way it’s being done above is just setting yourself up for some really nasty Objective C metadata generation depending on how complex
MyClass
is.
Note that all of the above generally applies to lambdas
as well. There is nothing magical about lambdas
that will save you. These are not lambda/block problems, they are threading problems. Blocks and lambdas just make it easier to get bitten by them.