Цель-C - Недостатки преодоления с C++?

Итак, сегодня мне стало скучно, и я решил поэкспериментировать с интерполяцией C++/Obj-C, и я нашел способ создать очень интересную установку.

@protocol NSCPPObj <NSObject>

-(id) init;
-(id) initWithInt:(int) value;
-(int) somethingThatReturnsAValue;
-(void) doSomething;

@end

class NSCPPObj : objc_object {
public:    
    static Class cls();

    int iVar;

    NSCPPObj();
    NSCPPObj(int);

    int somethingThatReturnsAValue();
    void doSomething();
};

Как видите, интерфейс довольно прост и понятен. Мы создаем два (почти) идентичных интерфейса, один для объекта C++, а другой для протокола Obj-C.

Теперь я нашел способ реализовать это, но приготовьтесь, это уродливо:

// NSCPPObj.mm
#import <objc/runtime.h>
#import <iostream>

#import "NSCPPObject.h"

Class NSCPPObj_class = nil;

__attribute__((constructor))
static void initialize()
{
    NSCPPObj_class = objc_allocateClassPair([NSObject class], "NSCPPObj", 0);

    class_addMethod(NSCPPObj_class->isa, @selector(alloc), imp_implementationWithBlock(^(id self) {
        return class_createInstance(NSCPPObj_class, sizeof(struct NSCPPObj));
    }), "@@:");

    class_addMethod(NSCPPObj_class, @selector(init), imp_implementationWithBlock(^(id self) {
        return self;        
    }), "@@:");

    class_addMethod(NSCPPObj_class, @selector(initWithInt:), imp_implementationWithBlock(^(id self, int value) {
        ((struct NSCPPObj *) self)->iVar = value;

        return self;
    }), "@@:i");

    class_addMethod(NSCPPObj_class, @selector(doSomething), imp_implementationWithBlock(^(id self) {
        ((struct NSCPPObj *) self)->doSomething();
    }), "v@:");
    class_addMethod(NSCPPObj_class, @selector(somethingThatReturnsAValue), imp_implementationWithBlock(^(id self) {
        return ((struct NSCPPObj *) self)->somethingThatReturnsAValue();
    }), "i@:");

    objc_registerClassPair(NSCPPObj_class);
}

Class NSCPPObj::cls()
{
    return NSCPPObj_class;
}

NSCPPObj::NSCPPObj()
{
    this->isa = NSCPPObj_class;
    [((id<NSCPPObj>) this) init];
}

NSCPPObj::NSCPPObj(int value)
{
    this->isa = NSCPPObj_class;
    [((id<NSCPPObj>) this) initWithInt:value];
}

void NSCPPObj::doSomething()
{
    std::cout << "Value Is: " << [((id<NSCPPObj>) this) somethingThatReturnsAValue] << std::endl;
}

int NSCPPObj::somethingThatReturnsAValue()
{
    return iVar;
}

Я подведу итог, что это делает:

  1. Выделяет пару классов
  2. Добавляет все методы класса и экземпляра к объекту
  3. Регистрирует класс Pair

Теперь, как вы можете видеть, это не очень гибко, но работает, и это улица с двусторонним движением:

id<NSCPPObj> obj = [[NSCPPObj::cls() alloc] initWithInt:15];
[obj doSomething];

NSLog(@"%i", [obj somethingThatReturnsAValue]);
NSLog(@"%@", obj);

NSCPPObj *objAsCPP = (__bridge NSCPPObj *) obj;

objAsCPP->doSomething();
std::cout << objAsCPP->somethingThatReturnsAValue() << std::endl;

Вы также можете создать объект, используя new NSCPPObj(15), но не забудьте удалить его! Очевидно, что это может работать в среде ARC или не-ARC, но ARC требует нескольких дополнительных бросков с мостовым соединением.

Итак, я подхожу к реальному вопросу:
Каковы плюсы / минусы этой структуры дизайна? Я могу перечислить несколько из верхней части моей головы:

Плюсы:

  1. Перегрузка операторов с C++
  2. Динамическое связывание метода с ObjC
  3. Может быть построен в стиле C++ или ObjC

Минусы:

  1. Трудно читаемая реализация
  2. Селекторы и привязки должны быть добавлены для каждой реализации C++, добавленной в интерфейс
  3. На объект класса нельзя ссылаться напрямую

Итак, после всего этого, вы бы порекомендовали эту структуру дизайна в приложении? и почему.

1 ответ

Решение

Итак, после всего этого, вы бы порекомендовали эту структуру дизайна в приложении? и почему.

Нет.

Это действительно хороший кусок кода; Мне особенно нравится использование imp_implementationWithBlock() (но я признаю, что могу быть неравнодушен к этой конкретной функции среды выполнения;). И, конечно же, подобные исследования всегда являются невероятно ценным инструментом обучения.

Проблема в контексте использования "реального платного проекта" заключается в том, что вы фактически создаете относительно общий мост, который затем должен иметь определенные мосты на обоих концах для взаимодействия либо с типичными библиотеками C++, либо с типичными API Objective-C / библиотеки. Другими словами, вы фактически создали новую среду выполнения, полученную из объединения двух существующих сред выполнения.

И, как вы указали в "Минусах", вам в значительной степени приходится прикасаться, оборачивать, изменять и / или отлаживать прокладки поверх каждого класса C++, который вы хотите использовать в этом шаблоне.

При работе с небольшим количеством кода Objective-C++ за последние 20 с лишним лет такой мост, как правило, доставляет больше хлопот, чем стоит. Скорее всего, вам лучше - потратить меньше времени на написание и отладку кода - создание простых оболочек Objective-C вокруг API C++ (или, откровенно говоря, C), которые затем могут быть интегрированы и использованы в целевых системах Objective-C.

Другие вопросы по тегам