Смешивание С# с помощью Objective-C
Я хотел бы использовать большую часть кода С# в качестве библиотеки для приложения Objective-C (Cocoa).
Я обнаружил проект MonoMac, который обертывает код Cocoa, но я предпочел бы иметь стандартное приложение Cocoa, написанное в Objective-C, которое может вызывать завернутый код С# (иначе).
В Windows я использую проект С++/CLI, который обертывает .NET-код и экспортирует простой старый интерфейс C для приложений на C/С++.
Есть ли простой способ достичь этого?
Ответы
Ответ 1
В Mac OS, очевидно, нет такого языка, как С++/CLI. В Windows С++/CLI фактически компилируется как управляемый код, запущенный CLR, который запускает собственный код; так как на Mac OS Mono не интегрирована в систему, это скорее наоборот. Ваше приложение является родным и может содержать управляемый код.
Mono предоставляет функции для размещения виртуальной машины CLR внутри процесса. Поскольку классы CLR не подвергаются прямому воздействию вашего C-кода, вы сможете вызывать методы объектов посредством отраженных вызовов.
Существует документация о том, как внедрить Mono в приложение на официальном сайте. Поскольку вы не заинтересованы в запуске .NET-программ напрямую, лучше прочитать "Вызов методов в CIL Universe" . В Mac OS вам нужно привязать к структуре Mono из вашей папки /Library/Frameworks
вместо использования pkg-config
.
Это действительно не должно заменять фактическое чтение вышеуказанного документа, но следующее можно рассматривать как руководство относительно того, чего ожидать:
#include <glib/glib.h>
#include <mono/jit/jit.h>
#include <mono-metadata/assembly.h>
#include <mono/metadata/debug-helpers.h>
// create an app domain
// http://en.wikipedia.org/wiki/Application_Domain
MonoDomain* domain = mono_jit_init("Domain");
// mandatory Cocoa call to show that Mono and ObjC work together
NSBundle* mainBundle = [NSBundle mainBundle];
NSString* dll = [mainBundle pathForResource:@"your-dll" ofType:@"dll"];
// load the referenced assembly in our domain
MonoAssembly* assembly = mono_domain_assembly_open(domain, [dll UTF8String]);
MonoImage* image = mono_assembly_get_image(assembly);
// find the class we want to wrap and create an uninitialized instance
MonoClass* classHandle = mono_class_from_name(image, "Name.Space", "YourClass");
MonoObject* object = mono_object_new(domain, classHandle);
// this calls the default, argument-less ctor
// for more complex constructors, you need to find the method handle and call it
// (helpful hint: constructors are internally called ".ctor", so the description
// string will look like "Namespace.Class..ctor()")
mono_runtime_object_init(object);
// get a method handle to whatever you like
const char* descAsString = "Your.NameSpace.YourClass:YourMethod()";
MonoMethodDesc* description = mono_method_desc_new(descAsString);
MonoMethod* method = mono_method_desc_search_in_class(description, classHandle);
// call it
void* args[0];
mono_runtime_invoke(method, object, args, NULL);
// when you're done, shutdown the runtime by destroying the app domain
mono_jit_cleanup(domain);
Если вы не найдете это очень привлекательным, вы можете пойти по-другому, как вы упомянули, и посмотреть в MonoMac, который обеспечивает привязки .NET к большой части API-интерфейсов, которые вы можете использовать в приложении Mac (Cocoa, CoreImage, CoreAnimation и т.д.) и означает создание собственных привязок.
Ответ 2
Если вы делаете это на Mac, то да, это возможно. На iOS не так много.
На Mac, если вы можете создать приложение CLI в MonoMac, вы можете вызвать свое приложение CLI из своего приложения Objective-C, используя NSTask. NSTask позволяет вам легко запустить инструмент командной строки, а затем захватить его вывод и взаимодействовать с ним. Чтобы сделать это, вы бы сделали что-то вроде:
NSArray *args = [NSArray arrayWithObjects:@"-arg1", @"-arg2", nil];
NSTask *foo = [[NSTask alloc] init];
[foo setLaunchPath:@"/usr/bin/foo"];
[foo setArguments:args];
NSPipe *pipe = [NSPipe pipe];
[foo setStandardOutput:pipe];
NSFileHandle *output = [pipe fileHandleForReading];
[foo launch];
NSData *data = [output readDataToEndOfFile];
NSString *outputStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"Got stuff: %@", outputStr);
Как правило, так, как вы хотите это сделать, вы включаете приложение CLI внутри своего пакета приложений. Затем вы можете получить путь к CLI-приложению, используя NSBundles -pathForResource: ofType: method.
iOS не включает API NSTask, поэтому это невозможно. Я слышал о том, как некоторые люди используют MonoTouch, чтобы сделать приложения для iOS с помощью С#, но, как вы сказали, я думаю, что вам лучше всего придерживаться Objective-C для большей части вашего приложения, если это возможно. Использование CLI-приложения, как вы описываете, определенно является опцией на Mac и может быть особенно полезно, когда у вас есть код, который уже написан и протестирован и работает, что вы просто хотите "обернуть" графическим интерфейсом Cocoa.
Итак, NSTask - это один из способов сделать это, используя внешний исполняемый файл CLI, завернутый в ваш пакет приложений. С другой стороны, вам может быть интересно, можете ли вы связать свой код С# непосредственно с Objective-C?
Ну, Objective-C является надмножеством C, и как таковой он обладает всеми возможностями C. Кроме того, если вы используете Objective-C ++, он также обладает всеми возможностями С++. Итак, ЕСЛИ вы можете получить MonoMac для создания статической библиотеки C или С++, тогда да, вы могли бы просто связать свою статическую библиотеку с кодом Objective-C Cocoa, и она будет работать. Я не могу сказать вам, как сделать библиотеку из MonoMac, но связать ее - это просто добавить ее в ваши связанные библиотеки в настройках сборки в Xcode.
EDIT: Я не знаком с С++/CLI как языком, и я неверно истолковал значение С++/CLI как "С++ Command Line Interface app". Тем не менее... описанные мной методы по-прежнему применяются как возможные методы для выполнения того, что вы хотите сделать, но НЕ используют интерфейс С++/CLI, как вы можете в Windows.