использование objc_msgSend для вызова функции Objective C с именованными аргументами - PullRequest
23 голосов
/ 04 апреля 2010

Я хочу добавить поддержку сценариев для проекта Objective-C, используя среду выполнения objc. Теперь я сталкиваюсь с проблемой, что у меня нет понятия, как я должен вызывать метод Objective-C, который принимает несколько именованных аргументов.

Так, например, следующий вызов target-c

[object foo:bar];

может быть вызвано из C с помощью:

objc_msgSend(object, sel_getUid("foo:"), bar);

Но как бы я сделал нечто подобное для вызова метода:

[object foo:var bar:var2 err:errVar];

??

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

Ответы [ 3 ]

45 голосов
/ 04 апреля 2010

Принятый ответ близок, но он не будет работать должным образом для определенных типов. Например, если объявлен метод, который принимает 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 - это. В базовой функции C для метода, который возвращает NSRect, первый аргумент фактически является указателем на выходное значение NSRect, а сама функция фактически возвращает void. Вы должны соответствовать этому соглашению при вызове, потому что это то, что будет вызывать вызываемый метод. Кроме того, обстоятельства, в которых используется objc_msgSend_stret, отличаются в разных архитектурах. Существует также objc_msgSend_fpret, который следует использовать для методов, которые возвращают определенные типы с плавающей запятой на определенных архитектурах.

Теперь, так как вы пытаетесь создать скрипт-мост, вы, вероятно, не можете явным образом привести все случаи, когда сталкиваетесь, вам нужно общее решение. В общем, это не совсем тривиально, и, к сожалению, ваш код должен быть специализирован для каждой архитектуры, на которую вы хотите ориентироваться (например, i386, x86_64, ppc). Лучше всего, вероятно, увидеть, как PyObjC делает это. Вы также захотите взглянуть на libffi . Вероятно, неплохо бы немного больше узнать о том, как параметры передаются в C, о чем вы можете прочитать в Mac OS X ABI Guide . Наконец, Грег Паркер, который работает во время выполнения objc, написал кучу очень хороших сообщений о внутренних объектах objc.

20 голосов
/ 04 апреля 2010
objc_msgSend(object, sel_getUid("foo:bar:err:"), var, var2, errVar);

Если одной из переменных является float, вам необходимо использовать метод @ Ken или cheat путем повторного интерпретации:

objc_msgSend(..., *(int*)&var, ...)

Кроме того, если селектор возвращает число с плавающей запятой, вам может понадобится использовать objc_msgSend_fpret, а если он возвращает структуру, вы должны использовать objc_msgSend_stret. Если это вызов суперкласса, вам нужно использовать objc_msgSendSuper2.

3 голосов
/ 04 апреля 2010

objc_msgSend (obj, @selector (foo: bar: err :), var, var2, & errVar);

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...