Objective-C++ ARC gotchas

This post has been published also in Coherent Labs’s blog – the company I co-founded and work for.

Lately I had to mix C++ and Objective-C pretty heavily (it’s called Objective-C++ apparently). What I usually need is having Obj-C objects inside C++ ones, which with “Automatic Reference Counting”(ARC) should work out-of-the-box. It actually does – except when it doesn’t!

In essence what the compiler probably does is just add ‘retain’ & ‘release’ calls in the proper places (constructors, destructors etc.). It is smart enough to recognize pointers to ARC objects and treat them as non-POD.

This is actually very cool and simplifies the interaction a lot. Alas there are some problems when you try to do some more ‘exotic’ stuff.

A pretty standard C++ way to use your own memory management routines is to allocate some memory with your custom allocator and then use placement new to construct an object in the fresh memory. Destruction goes the other way around – call the destructor explicitly and deallocate your memory.

class Test {};
int main()
{
  // create manually
  Test *t = static_cast<Test*>(operator new(sizeof(Test)));
  new(t) Test;

  // destroy manually
  t->~Test();
  operator delete(t);

  return 0;
}

In the following code, as you might expect, ‘dealloc’ is called as soon as the C++ object is destroyed – so no leak happens:

#include <iostream>
#import <Foundation/Foundation.h>

@interface TestObj : NSObject
@end

@implementation TestObj

-(id)init {
  self = [super init];
    NSLog(@"init!");
    return self;
}

-(void)dealloc {
  NSLog(@"dealloc!");
}

@end

class Parent
{
public:
  /*virtual */ void aFunc() {} // Uncomment 'virtual' for reference leak!
};

class TestClass : public Parent
{
public:
  TestClass() : m_Obj([[TestObj alloc] init])
  {}

private:
  TestObj* m_Obj;
};

int main(int argc, const char * argv[])
{
  TestClass* cl = new TestClass;

  cl->~TestClass();
  operator delete(cl);

  //delete cl; // Uncomment me and comment the manual mem. management to never leak!

  std::cout << "End!" << std::endl;

  return 0;
}

However if you uncomment the ‘virtual’ keyword and hence make the hierarchy virtual the ‘dealloc’ method will not be called. The compiler in this case does not create the non-trivial destructor required! If you substitute the manual memory management with the delete operator then the destructor is synthesized and ‘dealloc’ gets called. The behavior is also prevented if your class is non-POD in C++ terms.

Not having a virtual destructor in a virtual hierarchy is arguably very bad, however breaking the ARC promise is even worse. I admit that stumbling upon this issue is not very easy because it requires a lot of things to happen but still the leaking references it introduces are serious enough to prompt me to write about it.

I haven’t looked at clang’s source about this issue and I can’t find a rationale behind it in the docs so I think it’s an oversight and can only speculate why it happens. The version of the compiler that I currently work and saw the problem is: “Apple clang version 4.1 (tags/Apple/clang-421.11.66)”.

All that said, if you follow the basic C++ guidelines of having virtual destructors in your polymorphic hierarchies you should be fine when you try to mix C++ and Objective-C.

Advertisements

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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s