【问题标题】:Writing a C++ Wrapper around Objective-C围绕 Objective-C 编写 C++ Wrapper
【发布时间】:2011-07-27 03:34:19
【问题描述】:

我想在 OS X 上的 C++ 项目中调用和使用 Objective-C 类。现在是开始转向所有 Objective-C 的时候了,但我们需要一段时间才能做到这一点。

如何实现这一目标?任何人都可以阐明并提供一个例子吗?

【问题讨论】:

标签: c++ objective-c xcode wrapper objective-c++


【解决方案1】:

我认为是Phil Jordan 真正提出了最好的C++ 封装的Obj-C 公式。但是,我认为后者,由 Obj-C 包装的 C++ 更有用。我将在下面解释原因。

用 C++ 包装一个 Objective-C 对象

Person.h - Obj-C 标头

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

PersonImpl.h - C++ 头文件

namespace people {
    struct PersonImpl;

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

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

    private:
        PersonImpl *impl;
    };
}

Person.mm - Obj-C++ 实现

#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 包装一个 C++ 对象

我经常发现真正的问题不是让 C++ 与 Obj-C 代码对话,而是在两者之间来回切换,这让事情变得很糟糕。想象一个对象需要只有 C++ 的项目,但基本的对象细节是在 Obj-C 域中填写的。在这种情况下,我用 C++ 编写对象,然后制作它以便我可以在 Obj-C 中与它对话。

Person.h - C++ 头文件

namespace people
{
   struct PersonImpl;

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

   private:
      PersonImpl *impl;
   }
}

Person.cpp - C++ 实现

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-C++ 实现

@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

关于 void *

如果您想避免整个项目被.mm 文件乱扔,那么您踏入C++ 领域的那一刻就会感到有些痛苦。所以,我们只是说,如果您认为没有必要将您的 C++ 对象取出,或者用 C++ 对象重新构建 Obj-C 对象,您可以删除该代码。需要注意的是,第二次通过 void * 方法从 Obj-C 代码中删除 Person 实例时,最好使用复制构造函数制作自己的副本,否则指针将失效。

【讨论】:

    【解决方案2】:

    Objective-C++ 是 C++ 的超集,就像 Objective-C 是 C 的超集一样。OS X 上的 gcc 和 clang 编译器都支持它,并允许您从以下位置实例化和调用 Objective-C 对象和方法在 C++ 中。只要您在 C++ 模块的实现中隐藏 Objective-C 标头导入和类型,它就不会感染您的任何“纯”C++ 代码。

    .mm 是 Objective-C++ 的默认扩展名。 Xcode 会自动做正确的事情。

    因此,例如,以下 C++ 类返回自 1970 年 1 月 1 日以来的秒数:

    //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++ 的编译速度甚至比 C++ 还要慢,但正如您在上面看到的,将其使用隔离到少数桥接类中相对容易。

    【讨论】:

      【解决方案3】:

      首先将你的文件从 *.m 重命名为 *.mm,这样你就可以得到 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"];
      }
      

      【讨论】:

      • 如果你想变得更加时髦,你可以将 type_traits 子类化为 is_ObjC_obj,并专注于它;)
      【解决方案4】:

      看看这个问题Calling Objective-C method from C++ method?

      您将需要一些 Objective-C 类来包装代码并使用 C 函数公开。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-08-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-02-03
        • 1970-01-01
        相关资源
        最近更新 更多