Используя objc_msgSend для вызова функции Objective C с именованными аргументами
Я хочу добавить поддержку сценариев для проекта Objective-C, используя время выполнения objc. Теперь я сталкиваюсь с проблемой, что у меня нет подсказки, как я должен вызывать метод Objective-C, который принимает несколько именованных аргументов.
Итак, например, следующий вызов Objective-C
[object foo:bar];
можно вызвать из C с помощью:
objc_msgSend(object, sel_getUid("foo:"), bar);
Но как мне сделать что-то подобное для вызова метода:
[object foo:var bar:var2 err:errVar];
??
Лучший Маркус
Ответы
Ответ 1
objc_msgSend(object, sel_getUid("foo:bar:err:"), var, var2, errVar);
Если одна из переменных - это float
, вам нужно использовать метод @Ken или обмануть реинтерпрет-литой:
objc_msgSend(..., *(int*)&var, ...)
Кроме того, если селектор возвращает float, вам может потребоваться использовать objc_msgSend_fpret
, и если он вернет структуру, вы должны использовать objc_msgSend_stret
. Если это вызов суперкласса, вам нужно использовать objc_msgSendSuper2
.
Ответ 2
Принятый ответ близок, но он не будет работать должным образом для определенных типов. Например, если объявлен метод принятия float в качестве второго аргумента, это не сработает.
Чтобы правильно использовать objc_msgSend, вы должны отдать его соответствующему типу. Например, если ваш метод объявлен как
- (void)foo:(id)foo bar:(float)bar err:(NSError **)err
тогда вам нужно будет сделать что-то вроде этого:
void (*objc_msgSendTyped)(id self, SEL _cmd, id foo, float bar, NSError**error) = (void*)objc_msgSend;
objc_msgSendTyped(self, @selector(foo:bar:err:), foo, bar, error);
Попробуйте описанный выше случай с помощью objc_msgSend и выйдите из полученных аргументов. Вы не увидите правильные значения в вызываемой функции. Эта необычная ситуация с литьем возникает из-за того, что objc_msgSend не предназначен для вызова как обычная C-функция. Он (и должен быть) реализован в сборке и просто перескакивает на целевую функцию C после запуска с несколькими регистрами. В частности, нет последовательного способа ссылаться на любой аргумент, предшествующий первым двум изнутри objc_msgSend.
Другой случай, когда просто вызов objc_msgSend прямо не будет работать, это метод, который возвращает NSRect, скажем, потому что objc_msgSend не используется в этом случае, objc_msgSend_stret is. В базовой функции C для метода, который возвращает NSRect, первый аргумент фактически является указателем на значение NSRect, а сама функция фактически возвращает void. Вы должны сопоставлять это соглашение при вызове, потому что это будет использоваться вызываемым методом. Кроме того, обстоятельства, в которых используется objc_msgSend_stret, различаются между архитектурами. Существует также objc_msgSend_fpret, который должен использоваться для методов, возвращающих определенные типы с плавающей запятой на определенные архитектуры.
Теперь, поскольку вы пытаетесь создать скриптовый мост, вы, вероятно, не можете явно использовать все случаи, с которыми работаете, вам нужно общее решение. В целом, это не совсем тривиально, и, к сожалению, ваш код должен быть специализированным для каждой архитектуры, на которую вы хотите настроить таргетинг (например, i386, x86_64, ppc). Лучше всего, наверное, посмотреть, как это делает PyObjC. Вы также захотите взглянуть на libffi. Вероятно, неплохо понять немного больше о том, как параметры передаются в C, о которых вы можете прочитать в Руководство для Mac OS X ABI, Наконец, Грег Паркер, работающий в среде выполнения objc, написал кучу очень хороших сообщений для внутренних объектов objc.
Ответ 3
objc_msgSend (obj, @selector (foo: bar: err:), var, var2, & errVar);