Написание С++ Wrapper вокруг Objective-C

Я хочу позвонить и работать с классами Objective-C из проекта С++ на OS X. Пришло время начать движение по направлению ко всем Objective-C, но мы должны сделать это в течение некоторого времени.

Как это можно сделать? Может ли кто-нибудь пролить свет и привести пример?

Ответ 1

Objective-C ++ является надмножеством С++, так же как Objective-C является надмножеством C. Он поддерживается как gcc и clang в OS X и позволяет создавать экземпляры и вызывать объекты и методы Objective-C из С++. Пока вы скрываете импорт и типы заголовков Objective-C в рамках реализации модуля С++, он не будет заражать любой ваш "чистый" код на С++.

.mm - это расширение по умолчанию для Objective-C ++. Xcode автоматически выполнит правильную работу.

Итак, например, следующий класс С++ возвращает секунды с 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();

Вы быстро обнаружите, что Objective-C ++ еще медленнее компилировать, чем С++, но, как вы можете видеть выше, относительно легко выделить его использование для небольшого количества классов мостов.

Ответ 2

Я думаю, что это было Phil Jordan, которые действительно выдвинули лучшую С++ завернутую форму Obj-C. Однако, я думаю, последний, С++, обернутый Obj-C, более полезен. Я объясню, почему ниже.

Обтекание объекта Objective-C с помощью С++

Person.h - заголовок Obj-C

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

PersonImpl.h - заголовок С++

namespace people {
    struct PersonImpl;

    class Person
    {
    public:
        Person();
        virtual ~Person();

        std::string name();
        void setName(std::string name);

    private:
        PersonImpl *impl;
    };
}

Person.mm - реализация Obj-С++

#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

Обтекание объекта С++ с помощью Objective-C

Я часто нахожу, что реальная проблема заключается не в том, чтобы С++ не разговаривал с кодом Obj-C, он переключался между двумя, где вещи становятся уродливыми. Представьте себе объект, в котором должны быть только объекты С++, но основные объекты детали заполняются на земле Obj-C. В этом случае я пишу объект в С++, а затем создаю его, чтобы я мог поговорить с ним в Obj-C.

Person.h - заголовок С++

namespace people
{
   struct PersonImpl;

   class Person
   {
   public:
      Person();
      Person(Person &otherPerson);
      ~Person();
      std:string name;

   private:
      PersonImpl *impl;
   }
}

Person.cpp - реализация С++

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 - заголовок Obj-C

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

Person.mm - реализация Obj-С++

@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

Относительно пустоты *

Второе, что вы вошли в землю С++, вы почувствовали некоторую боль, если хотите, чтобы весь проект не был завален файлами .mm. Итак, позвольте сказать, если вы не думаете, что когда-либо понадобится вернуть ваш объект С++ или восстановить объект Obj-C с объектом С++, вы можете удалить этот код. Важно отметить, что во втором случае вы удаляете экземпляр Person из кода Obj-C с помощью метода void *, который вам лучше создать свою собственную копию с помощью конструктора копирования или указатель станет недействительным.

Ответ 3

Сначала переименуйте файлы с *.m на *.M, чтобы получить Objective-C ++

Я не устал, так что это спекуляция (я буду сегодня):

Поскольку все объекты Objective-C ++ (которые подсчитываются по ссылке) управляются с помощью указателей, поэтому вы можете написать специальный деструктор для общего указателя.

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

Теперь вы можете вставлять свои Objective-C объекты в boost:: shared_ptr

// FuncFile.M
//
int func()
{
    boost::shared_ptr<MyX, ObjCDestruct<MyX> >  data([[MyX alloc] init]);

    [data.get() doAction1:@"HI"];
}