Можно ли использовать функцию C в качестве селектора в Какао? - PullRequest
6 голосов
/ 31 августа 2009

Я хочу начать новый поток, используя функцию C, а не метод target-C.Я попытался

[NSThread detachNewThreadSelector: @selector(func) toTarget: nil withObject: id(data)];

, где у меня есть

void func(void *data) {
   // ...
}

, а data - это void *, но я получаю аварийное завершение в objc_msgSend, вызванное из

-[NSThread initWithTarget:selector:object:]

Что я могу сделать вместо этого?Это вообще возможно?

Ответы [ 5 ]

10 голосов
/ 31 августа 2009

Сверните свои собственные:

// In some .h file.  #import to make the extension methods 'visible' to your code.
@interface NSThread (FunctionExtension)
+(void)detachNewThreadByCallingFunction:(void (*)(void *))function data:(void *)data;
-(id)initWithFunction:(void (*)(void *))function data:(void *)data;
@end

// In some .m file.
@implementation NSThread (FunctionExtension)

+(void)startBackgroundThreadUsingFunction:(id)object
{
  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

  void (*startThreadFunction)(void *) = (void (*)(void *))[[object objectForKey:@"function"] pointerValue];
  void *startThreadData               = (void *)          [[object objectForKey:@"data"] pointerValue];

  if(startThreadFunction != NULL) { startThreadFunction(startThreadData); }

  [pool release];
  pool = NULL;
}

+(void)detachNewThreadByCallingFunction:(void (*)(void *))function data:(void *)data
{
  [[[[NSThread alloc] initWithFunction:function data:data] autorelease] start];
}

-(id)initWithFunction:(void (*)(void *))function data:(void *)data
{
  return([self initWithTarget:[NSThread class] selector:@selector(startBackgroundThreadUsingFunction:) object:[NSDictionary dictionaryWithObjectsAndKeys:[NSValue valueWithPointer:function], @"function", [NSValue valueWithPointer:data], @"data", NULL]]);
}

@end

ПРИМЕЧАНИЕ : я написал приведенный выше код и разместил его в открытом доступе. (иногда юристам нравятся подобные вещи) Это также полностью не проверено!

Вы всегда можете удалить биты NSAutoreleasePool, если вы можете гарантировать, что функция входа в поток также создает ее ... но она безвредна, не имеет никакого ухудшения скорости и делает вызов произвольных функций C намного более проще. Я бы сказал, просто держи это там.

И вы можете использовать его так:

void bgThreadFunction(void *data)
{
  NSLog(@"bgThreadFunction STARTING!! Data: %p", data);
}

-(void)someMethod
{
  // init and then start later...
  NSThread *bgThread = [[[NSThread alloc] initWithFunction:bgThreadFunction data:(void *)0xdeadbeef] autorelease];
  // ... assume other code/stuff here.
  [bgThread start];

  // Or, use the all in one convenience method.
  [NSThread detachNewThreadByCallingFunction:bgThreadFunction data:(void *)0xcafebabe];
}

При запуске:

2009-08-30 22:21:12.529 test[64146:1303] bgThreadFunction STARTING!! Data: 0xdeadbeef
2009-08-30 22:21:12.529 test[64146:2903] bgThreadFunction STARTING!! Data: 0xcafebabe
3 голосов
/ 31 августа 2009

Ну, я не уверен, возможно ли это, но имейте в виду, что у каждого метода Objective-C есть два неявных / скрытых аргумента, self и _cmd. IMP обычно typedef, вот так:

typedef id (*IMP)(id,SEL,...);

Если вы хотите использовать методы и селекторы jerry-rig, вам нужен метод, который выглядит следующим образом:

void func (id self, SEL _cmd, void *firstParameter);

Но даже после этого вам нужно зарегистрировать имя селектора во время выполнения, затем вам нужно связать этот селектор с методом, но это делается для каждого класса в отдельности (т.е. классы могут иметь разные реализации то же имя селектора), так что вам, по крайней мере, нужен фиктивный класс.

Намного проще создать фиктивный класс и фиктивный экземпляр этого класса, чем вызывать различные API времени выполнения только для того, чтобы NSThread вызвал одну функцию Си.

3 голосов
/ 31 августа 2009

Создайте класс Objective-C с методом, который просто вызывает эту функцию. Возьмите селектор этого метода и передайте его NSThread API.

1 голос
/ 31 августа 2009

Если вам не нужны NSThread вещи, вы также можете запустить поток с прямым интерфейсом POSIX .

0 голосов
/ 18 мая 2014

Я хочу начать новый поток, используя функцию C, а не метод target-C

Тогда почему бы вам просто не использовать:

  1. нити POSIX,
  2. НОД

dispatch_async_f() (человек) подходит именно для этой цели.

...