Подтвердить что ты не робот

Используя 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];

??

Лучший Маркус

4b9b3361

Ответ 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);