Questions on how to use Objective C in JUCE-based C++ classes (Memory management, autoreleaspools, juce_osx_ObjCHelpers)


#1

Okay, I started this thread yesterday trying to fix an error in some Objective C wrapper code I wrote. However, to me it seems that I’m lacking some basic knowledge on how to write such a class in a proper way.

So instead of asking for help for my specific error I want to ask differently: Can someone give a quick outline of how to implement such a class correctly?

Maybe you could guide me with some example. Let’s say I want to create some kind of string array class that uses an NSArray internally. I know this is nothing really senseful but it might serve as some good example for some of my questions

// StringArrayUsingNSArray.h

#include <juce_core/juce_core.h>

class StringArrayUsingNSArray
{
public:
    StringArrayUsingNSArray (juce::StringArray& arrayToInitializeFrom);
    juce::String getStringAtIdx (int i);

private:
    class Pimpl;
    std::unique_ptr<Pimpl> pimpl;
};

// StringArrayUsingNSArray.mm

#import <Foundation/Foundation.h>
#include <juce_core/native/juce_osx_ObjCHelpers.h>
#include "StringArrayUsingNSArray.h"

class StringArrayUsingNSArray::Pimpl
{
public:
    Pimpl (juce::StringArray& arrayToInitializeFrom)
    {
        NSMutableArray* mutableArray = [NSMutableArray new]; // any autorelease needed here?
        for (auto& string : arrayToInitializeFrom)
            [mutableArray addObject: juce::juceStringToNS (string)];

        nsArray = mutableArray; // what about object ownership here?
    }
    
    ~Pimpl ()
    {
        // Any cleanup needed here?
    }

    juce::String getStringAtIdx (int i)
    {
        return juce::nsStringToJuce (nsArray[static_cast<NSUInteger> (i)]);
    }

private:
    NSArray* nsArray;
};

StringArrayUsingNSArray::StringArrayUsingNSArray (juce::StringArray& arrayToInitializeFrom)
{
    pimpl.reset (new Pimpl (arrayToInitializeFrom));
}

juce::String StringArrayUsingNSArray::getStringAtIdx (int i)
{
    return pimpl->getStringAtIdx (i);
}

First Topic: Memory management / Object lifetime of Cocoa class Members
I read a lot regarding the autorelease mechanisms and it just feels quite difficult for me as a C++ thinking person to get a feeling for what happens when. To me as a C++ guy there should be something like [nsArray release] in the destructor of my Pimpl class.

However, there is this autoreleasepool that every juce thread creates (if I got it right). Now is the nsArray object created in the constructor automatically added to the autoreleasepool of the thread invoking this constructor? What happens if the constructor is invoked on a different thread than the desctructor or the autoreleasepool of the constructing thread goes out of scope while the class is still accessed by other threads?

Second Topic: Memory management / Object lifetime of temporary created objcets
I’ve seen inside juceStringToNS that it returns the NSString with an autorelease call, so who is responsible to delete this string after it has been added to the array? Is the string copied to the array and then the temporary created string is released by the autoreleasepool of the thread that invokes the constructor? Or will the ownership of the string be transferred to the array and it will release the string when the array is released? Or is it something completely different?

And is the assignment of the temporary created mutable array to the array class member correct?

Third Topic: Usage of juce_osx_ObjCHelpers.h
Including the juce_osx_ObjCHelpers.h file like above generates the following compiler errors:

Error:(204, 45) use of undeclared identifier 'objc_msgSendSuper'; did you mean 'ObjCMsgSendSuper'?
Error:(210, 95) use of undeclared identifier 'objc_msgSendSuper'
Error:(214, 95) use of undeclared identifier 'objc_msgSend_fpret'
Error:(292, 20) variable has incomplete type 'juce::objc_super'
Error:(301, 9) use of undeclared identifier 'object_getInstanceVariable'
Error:(341, 20) variable has incomplete type 'juce::objc_super'
Error:(353, 13) use of undeclared identifier 'object_setInstanceVariable'
Error:(356, 20) variable has incomplete type 'juce::objc_super'

Seems like I’m using it somehow wrong. What’s the right way to include this header? Note that all this is implemented in the context of a custom juce module.

I’d really love to get some answers to gain a bit more understanding of all this Obj-C memory management stuff and how it bridges cleanly to a JUCE-based C++ context.