Writing a C++ Wrapper around Objective-C

后端 未结 4 1836
臣服心动
臣服心动 2020-12-30 08:58

I want to call and work with Objective-C classes from within a C++ project on OS X. It is time to start moving towards all Objective-C, but we need to do this over some time

相关标签:
4条回答
  • 2020-12-30 09:04

    First rename your files from *.m to *.mm so you get Objective-C++

    I have not tired this, so it is speculation (I will tonight):

    As all Objective-C++ objects (that are reference counted) are controlled via pointers so you can write a special destructor for shared pointer.

    template<typename T>
    struct ObjCDestruct
    {
        void operator()(T* obj)
        {
            [obj release];
        }
    };
    

    Now you can stick your Objective-C obects in a boost::shared_ptr

    // FuncFile.M
    //
    int func()
    {
        boost::shared_ptr<MyX, ObjCDestruct<MyX> >  data([[MyX alloc] init]);
    
        [data.get() doAction1:@"HI"];
    }
    
    0 讨论(0)
  • 2020-12-30 09:05

    Objective-C++ is a superset of C++, just as Objective-C is a superset of C. It is supported by both the gcc and clang compilers on OS X and allows you to instantiate and call Objective-C objects & methods from within C++. As long as you hide the Objective-C header imports and types within the implementation of a C++ module, it won't infect any of your "pure" C++ code.

    .mm is the default extension for Objective-C++. Xcode will automatically do the right thing.

    So, for example, the following C++ class returns the seconds since Jan 1., 1970:

    //MyClass.h
    
    class MyClass
    {
      public:
        double secondsSince1970();
    };
    
    //MyClass.mm
    
    #include "MyClass.h"
    #import <Foundation/Foundation.h>
    
    double MyClass::secondsSince1970()
    {
      return [[NSDate date] timeIntervalSince1970];
    }
    
    //Client.cpp
    
    ...
    MyClass c;
    double seconds = c.secondsSince1970();
    

    You will quickly find that Objective-C++ is even slower to compile than C++, but as you can see above, it's relatively easy to isolate its usage to a small number of bridge classes.

    0 讨论(0)
  • 2020-12-30 09:05

    Check out this question Calling Objective-C method from C++ method?

    You will need to have some objective-c classes to wrap the code and expose with a C function.

    0 讨论(0)
  • 2020-12-30 09:09

    I think it was Phil Jordan that really put forward the best C++ wrapped Obj-C formula. However, I think the latter, C++ wrapped by Obj-C is more useful. I'll explain why below.

    Wrapping an Objective-C object with C++

    Person.h - The Obj-C header

    @interface Person : NSObject
    @property (copy, nonatomic) NSString *name;
    @end
    

    PersonImpl.h - The C++ header

    namespace people {
        struct PersonImpl;
    
        class Person
        {
        public:
            Person();
            virtual ~Person();
    
            std::string name();
            void setName(std::string name);
    
        private:
            PersonImpl *impl;
        };
    }
    

    Person.mm - The Obj-C++ implementation

    #import "Person.h"
    
    #import "PersonImpl.h"
    
    namespace people {
       struct PersonImpl
       {
          Person *wrapped;
       };
    
       Person::Person() :
       impl(new PersonImpl())
       {
          impl->wrapped = [[Person alloc] init];
       }
    
       Person::~Person()
       {
          if (impl) {
             [impl->wrapped release]; // You should turn off ARC for this file. 
                                      // -fno-objc-arc in Build Phases->Compile->File options
          }
    
          delete impl;
       }
    
       std::string Person::name()
       {
          return std::string([impl->wrapped UTF8String]); 
       }
    
       void Person::setName(std::string name)
       {
          [impl->wrapped setName:[NSString stringWithUTF8String:name.c_str()]];
       }
    }
    
    @implementation Person
    @end
    

    Wrapping a C++ object with Objective-C

    I often find the real problem isn't getting C++ to talk to Obj-C code, it's switching back and forth between the two where things get ugly. Imagine an object that needs to have C++-only items, but the basic object details are filled out in Obj-C land. In this case, I write the object in C++ and then make it so I can talk to it in Obj-C.

    Person.h - The C++ header

    namespace people
    {
       struct PersonImpl;
    
       class Person
       {
       public:
          Person();
          Person(Person &otherPerson);
          ~Person();
          std:string name;
    
       private:
          PersonImpl *impl;
       }
    }
    

    Person.cpp - The C++ implementation

    namespace people
    {
       struct PersonImpl
       {
           // I'll assume something interesting will happen here.
       };
    
       Person::Person() :
       impl(new PersonImpl())
       {
       }
    
       Person::Person(Person &otherPerson) :
       impl(new PersonImpl()),
       name(otherPerson.name)
       {
       }
    
       ~Person()
       {
          delete impl;
       }
    }
    

    Person.h - The Obj-C header

    @interface Person : NSObject
    @property (unsafe_unretained, nonatomic, readonly) void *impl;
    @property (copy, nonatomic) NSString *name;
    @end
    

    Person.mm - The Obj-C++ implementation

    @interface Person ()
    @property (unsafe_unretained, nonatomic) std::shared_ptr<people::Person> impl;
    @end
    
    @implementation Person
    
    - (instancetype)init
    {
       self = [super init];
       if (self) {
          self.impl = std::shared_ptr<people::Person>(new people::Person());
       }
    
       return self;
    }
    
    - (instancetype)initWithPerson:(void *)person
    {
       self = [super init];
       if (self) {
          people::Person *otherPerson = static_cast<people::Person *>(person);
          self.impl = std::shared_ptr<people::Person>(new people::Person(*otherPerson));
       }
    
       return self;
    }
    
    - (void)dealloc
    {
       // If you prefer manual memory management
       // delete impl;
    }
    
    - (void *)impl
    {
       return static_cast<void *>(self.impl.get());
    }
    
    - (NSString *)name
    {
       return [NSString stringWithUTF8String:self.impl->name.c_str()];
    }
    
    - (void)setName:(NSString *)name
    {
       self.impl->name = std::string([name UTF8String]);
    }
    
    @end
    

    Regarding the void *

    The second you stepped into the land of C++ you were going to feel some pain if you want to avoid your whole project being littered with .mm files. So, let's just say, if you don't think it is ever necessary to get your C++ object back out, or reconstitute the Obj-C object with the C++ object you can remove that code. It is important to note that the second you remove the Person instance from the Obj-C code through the void * method you had better make your own copy with the copy constructor or the pointer will become invalid.

    0 讨论(0)
提交回复
热议问题