Функциональные указатели в Objective C

13
задан Sam Stewart 22 November 2009 в 12:28
поделиться

4 ответа

Как правило, для обратного вызова в Objective-C требуются две части информации; вызываемый метод и объект для его вызова. Ни просто селектор, ни просто IMP - результат instanceMethodForSelector: - не будет достаточной информацией.

Большинство API обратного вызова предоставляют указатель контекста, который обрабатывается как непрозрачное значение, которое передается через Перезвоните. Это ключ к вашей загадке.

То есть, если у вас есть функция обратного вызова, объявленная как:

typedef void (*CallBackFuncType)(int something, char *else, void *context);

И некоторый API, который использует указатель указанного типа функции обратного вызова:

void APIThatWillCallBack(int f1, int f2, CallBackFuncType callback, void *context);

Тогда вы бы реализовали свой обратный вызов примерно как это:

void MyCallbackDude(int a, char *b, void *context) {
    [((MyCallbackObjectClass*)context) myMethodThatTakesSomething: a else: b];
}

И затем вы могли бы назвать API чем-то вроде этого:

MyCallbackObjectClass *callbackContext = [MyCallbackObjectClass new];
APIThatWillCallBack(17, 42, MyCallbackDude, (void*)callbackContext);

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

15
ответ дан 1 December 2019 в 20:29
поделиться

Вы можете создать класс C ++ как оболочку. И вызовите сообщение цели C из этого класса. Просто не забудьте сделать свой файл yoursource.mm вместо yoursource.cpp .

1
ответ дан 1 December 2019 в 20:29
поделиться

Вы можете сделать это с помощью instanceMethodForSelector: Дополнительную информацию см. В документации Apple NSObject .

1
ответ дан 1 December 2019 в 20:29
поделиться

Вы можете сделать это с помощью @selector и типа SEL :

SEL sel = @selector(myMethod:);

/* Equivalent to [someObject myMethod:Paramater] */
[someObject performSelector:sel withObject:Parameter]

Существует также тип IMP . ..

IMP imp = [someObject methodForSelector:sel];

/* don't remember if this syntax is correct; it's been a while...
 * the idea is that it's like a function pointer. */
imp(someObject, sel, Parameter); 

Обновление на основе ваших комментариев

Если вы не хотите указывать объект, в этом случае вы просите что-то ужасно непереносимое. По сути, вам нужны лямбда-выражения, которые не являются особенностью C, хотя они входят в C ++ 0x.

Так что мое решение будет «незаметным», схематичным и непереносимым ...

НО. .. возможно, вы сможете сделать это с помощью генерации кода времени выполнения ...

Вы можете начать с написания функции-заглушки в ассемблере (при условии, что вам нужен x86)

push dword 0 ; SEL will go here
push dword 0 ; Object will go here
push dword 0 ; IMP will go here
pop eax      ; eax = imp
call eax     ; call imp
add esp, 8   ; cleanup stack
ret          ; return

Это собирается в:

0068 0000 6800 0000 0000 0068 0000 5800 d0ff c481 0004 0000 00c3

Обратите внимание на инструкцию push dword 0 - это байты 68 00 00 00 00 . Мы будем вводить нули в указатель во время выполнения.

Итак, мы можем скопировать это в буфер malloc () d, исправить его и вызвать mprotect () , чтобы сделать его исполняемым.

Код ниже предназначен для иллюстративных целей, а я понятия не имею, работает ли это. : -)

/* x86 code... */
char code[] = { 0x68, 0x00, 0x00, 0x00, 0x00, 0x68,
                0x00, 0x00, 0x00, 0x00, 0x68, 0x00,
                0x00, 0x00, 0x00, 0x58, 0xff, 0xd0,
                0x81, 0xc4, 0x04, 0x00, 0x00, 0x00,
                0xc3 };

char *buf = malloc(sizeof(code));

SEL Selector = @selector(Method);
IMP Imp = [object methodForSelector:Selector];

/* Copy template */
memcpy(buf, code, sizeof(code));

/* Patch the "push dword 0" parts with your arguments
 * This assumes everything is 32-bit, including SEL, IMP, etc. */
memcpy(buf + 1, &Selector, sizeof(Selector));
memcpy(buf + 6, &object, sizeof(object));
memcpy(buf + 11, &Imp, sizeof(Imp));

/* Now here comes the sketchy part...
 * Make it executable and turn it into a function pointer.  */
mprotect(buf, sizeof(code), PROT_EXEC);
void (*Function)() = (void(*)())buf;

/* Now, crazy as it sounds, you should be able to do: */
Function();

Возможно, вы захотите сделать [сохранение объекта] до тех пор, пока существует эта функция, и [освобождение объекта] , когда и если вы захотите освободить его . (Возможно, лучше всего обернуть эту эскизность внутри объекта, а затем использовать обычный счет ссылок objc для управления буфером и ссылку на объект .) Возможно, вы также захотите использовать mmap () выделить вместо malloc () ...

Если это звучит излишне сложно, это потому, что это так. : -)

и вызовите mprotect () , чтобы сделать его исполняемым.

Код ниже предназначен для иллюстративных целей, и я не знаю, работает ли он. : -)

/* x86 code... */
char code[] = { 0x68, 0x00, 0x00, 0x00, 0x00, 0x68,
                0x00, 0x00, 0x00, 0x00, 0x68, 0x00,
                0x00, 0x00, 0x00, 0x58, 0xff, 0xd0,
                0x81, 0xc4, 0x04, 0x00, 0x00, 0x00,
                0xc3 };

char *buf = malloc(sizeof(code));

SEL Selector = @selector(Method);
IMP Imp = [object methodForSelector:Selector];

/* Copy template */
memcpy(buf, code, sizeof(code));

/* Patch the "push dword 0" parts with your arguments
 * This assumes everything is 32-bit, including SEL, IMP, etc. */
memcpy(buf + 1, &Selector, sizeof(Selector));
memcpy(buf + 6, &object, sizeof(object));
memcpy(buf + 11, &Imp, sizeof(Imp));

/* Now here comes the sketchy part...
 * Make it executable and turn it into a function pointer.  */
mprotect(buf, sizeof(code), PROT_EXEC);
void (*Function)() = (void(*)())buf;

/* Now, crazy as it sounds, you should be able to do: */
Function();

Возможно, вы захотите сделать [сохранение объекта] , пока существует эта функция, и [освобождение объекта] , когда и если вам следует освободить его . (Вероятно, лучше всего обернуть эту эскизность внутри объекта, а затем использовать обычный счет ссылок objc для управления буфером и ссылку на объект .) Возможно, вы также захотите использовать mmap () выделить вместо malloc () ...

Если это звучит излишне сложно, это потому, что это так. : -)

и вызовите mprotect () , чтобы сделать его исполняемым.

Код ниже предназначен для иллюстративных целей, и я не знаю, работает ли он. : -)

/* x86 code... */
char code[] = { 0x68, 0x00, 0x00, 0x00, 0x00, 0x68,
                0x00, 0x00, 0x00, 0x00, 0x68, 0x00,
                0x00, 0x00, 0x00, 0x58, 0xff, 0xd0,
                0x81, 0xc4, 0x04, 0x00, 0x00, 0x00,
                0xc3 };

char *buf = malloc(sizeof(code));

SEL Selector = @selector(Method);
IMP Imp = [object methodForSelector:Selector];

/* Copy template */
memcpy(buf, code, sizeof(code));

/* Patch the "push dword 0" parts with your arguments
 * This assumes everything is 32-bit, including SEL, IMP, etc. */
memcpy(buf + 1, &Selector, sizeof(Selector));
memcpy(buf + 6, &object, sizeof(object));
memcpy(buf + 11, &Imp, sizeof(Imp));

/* Now here comes the sketchy part...
 * Make it executable and turn it into a function pointer.  */
mprotect(buf, sizeof(code), PROT_EXEC);
void (*Function)() = (void(*)())buf;

/* Now, crazy as it sounds, you should be able to do: */
Function();

Возможно, вы захотите сделать [сохранение объекта] до тех пор, пока существует эта функция, и [освобождение объекта] , когда и если вы захотите освободить его . (Возможно, лучше всего обернуть эту эскизность внутри объекта, а затем использовать обычный счет ссылок objc для управления буфером и ссылку на объект .) Возможно, вы также захотите использовать mmap () выделить вместо malloc () ...

Если это звучит излишне сложно, это потому, что это так. : -)

-)

/* x86 code... */
char code[] = { 0x68, 0x00, 0x00, 0x00, 0x00, 0x68,
                0x00, 0x00, 0x00, 0x00, 0x68, 0x00,
                0x00, 0x00, 0x00, 0x58, 0xff, 0xd0,
                0x81, 0xc4, 0x04, 0x00, 0x00, 0x00,
                0xc3 };

char *buf = malloc(sizeof(code));

SEL Selector = @selector(Method);
IMP Imp = [object methodForSelector:Selector];

/* Copy template */
memcpy(buf, code, sizeof(code));

/* Patch the "push dword 0" parts with your arguments
 * This assumes everything is 32-bit, including SEL, IMP, etc. */
memcpy(buf + 1, &Selector, sizeof(Selector));
memcpy(buf + 6, &object, sizeof(object));
memcpy(buf + 11, &Imp, sizeof(Imp));

/* Now here comes the sketchy part...
 * Make it executable and turn it into a function pointer.  */
mprotect(buf, sizeof(code), PROT_EXEC);
void (*Function)() = (void(*)())buf;

/* Now, crazy as it sounds, you should be able to do: */
Function();

Возможно, вы захотите сделать [сохранение объекта] , пока существует эта функция, и [освобождение объекта] , когда и если вы должны выбрать его освобождение. (Вероятно, лучше всего обернуть эту эскизность внутри объекта, а затем использовать обычный счет ссылок objc для управления буфером и ссылку на объект .) Возможно, вы также захотите использовать mmap () выделить вместо malloc () ...

Если это звучит излишне сложно, это потому, что это так. : -)

-)

/* x86 code... */
char code[] = { 0x68, 0x00, 0x00, 0x00, 0x00, 0x68,
                0x00, 0x00, 0x00, 0x00, 0x68, 0x00,
                0x00, 0x00, 0x00, 0x58, 0xff, 0xd0,
                0x81, 0xc4, 0x04, 0x00, 0x00, 0x00,
                0xc3 };

char *buf = malloc(sizeof(code));

SEL Selector = @selector(Method);
IMP Imp = [object methodForSelector:Selector];

/* Copy template */
memcpy(buf, code, sizeof(code));

/* Patch the "push dword 0" parts with your arguments
 * This assumes everything is 32-bit, including SEL, IMP, etc. */
memcpy(buf + 1, &Selector, sizeof(Selector));
memcpy(buf + 6, &object, sizeof(object));
memcpy(buf + 11, &Imp, sizeof(Imp));

/* Now here comes the sketchy part...
 * Make it executable and turn it into a function pointer.  */
mprotect(buf, sizeof(code), PROT_EXEC);
void (*Function)() = (void(*)())buf;

/* Now, crazy as it sounds, you should be able to do: */
Function();

Возможно, вы захотите сделать [сохранение объекта] , пока существует эта функция, и [освобождение объекта] , когда и если вы должны выбрать его освобождение. (Вероятно, лучше всего обернуть эту эскизность внутри объекта, а затем использовать обычный счет ссылок objc для управления буфером и ссылку на объект .) Возможно, вы также захотите использовать mmap () выделить вместо malloc () ...

Если это звучит излишне сложно, это потому, что это так. : -)

Я также захочу использовать mmap () для выделения вместо malloc () ...

Если это звучит излишне сложно, это потому, что это так. : -)

Я также захочу использовать mmap () для выделения вместо malloc () ...

Если это звучит излишне сложно, это потому, что это так. : -)

13
ответ дан 1 December 2019 в 20:29
поделиться
Другие вопросы по тегам:

Похожие вопросы: