Написание С++ 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"];
}
Ответ 4
Проверьте этот вопрос Вызов метода Objective-C из метода С++?
Вам понадобится несколько классов Objective-C, чтобы обернуть код и открыть с помощью функции C.