C++ classes as instance variables of an Objective-

2020-06-04 09:15发布

问题:

I need to mix Objective-C and C++. I would like to hide all the C++ stuff inside one class and keep all the others plain Objective-C. The problem is that I want to have some C++ classes as instance variables. This means they have to be mentioned in the header file, which gets included by other classes and C++ starts spreading to the whole application. The best solution I was able to come with so far looks like this:

#ifdef __cplusplus
#import "cppheader.h"
#endif

@interface Foo : NSObject
{
    id regularObjectiveCProperty;
    #ifdef __cplusplus
    CPPClass cppStuff;
    #endif
}

@end

This works. The implementation file has an mm extension, so that it gets compiled as Objective-C mixed with C++, the #ifdef unlocks the C++ stuff and there we go. When some other, purely Objective-C class imports the header, the C++ stuff is hidden and the class does not see anything special. This looks like a hack, is there a better solution?

回答1:

This sounds like a classic use for an interface/@protocol. Define an objective-c protocol for the API and then provide an implementation of that protocol using your Objective-C++ class. This way clients need only know about the protocol and not the header of the implementation. So given the original implementation

@interface Foo : NSObject
{
    id regularObjectiveCProperty;
    CPPClass cppStuff;

}

@end

I would define a protocol

//Extending the NSObject protocol gives the NSObject
// protocol methods. If not all implementations are
// descended from NSObject, skip this.
@protocol IFoo <NSObject>

// Foo methods here
@end

and modify the original Foo declaration to

@interface Foo : NSObject <IFoo>
{
    id regularObjectiveCProperty;
    CPPClass cppStuff;
}

@end

Client code can then work with type id<IFoo> and does not need to be compiled as Objective-C++. Obviously you can pass an instance of Foo to these clients.



回答2:

I also ran into this issue recently. In my case a protocol was overkill. I just needed to keep a pointer to a data access object that happened to be a C++ object.

What I did was declare the class with a void * instance variable and cast it when I use it in the instance methods.

This is a little bit hack-y, but conceptually, it's very similar to what the Objective-C id type is.



回答3:

Is there some particular reason you cannot just use Objective C++ for everything? Simply switch the compiler to Compile Sources As: Objective C++ (or rename all your source files from .cpp or .m to .mm). Then you can freely intermix your C++ and Objective C.

C++ starts spreading to the whole application

What problem is there with that? If your Objective C code is doing only C/Objective C code in general, then it will almost certainly not be affected at all by being compiled as C++. There is no appreciable size or speed performance issues.

The only two downsides I've found are: you cannot (yet) use clang static analyser to analyseC++; some (relatively weird) C code wont work in C++, which is occasionally an issue when using third party C code.



回答4:

You might find that you have problems doing this -- from what I remember of ObjectiveC++ you may find that the constructor and the destructor for your enclosed C++ object won't get called.



回答5:

DO NOT DO THIS

If you ifdef out an instance variable, that will give two separate instance variable layouts for this class. You will get random memory smashers all over the place because memory allocated for the object in half the cases will be too short. Instead of ifdefing out the instance variable, forward-declare its type like

struct CPPClass;

and have a pointer to it in the ivar, then in your init/dealloc methods call new/delete to create the object. If you have several objects, you can create a struct to hold all C++ ivars directly and then just new/delete that struct.

See this thread for more detail and further links to information, including a podcast that talks at length about ObjC++: Can I separate C++ main function and classes from Objective-C and/or C routines at compile and link?