6

我想使用 C 函数而不是 Objective-C 方法来启动一个新线程。我试过了

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

我在哪里

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

并且data是一个void *,但我得到一个运行时崩溃objc_msgSend,调用自

-[NSThread initWithTarget:selector:object:]

我能做些什么呢?甚至可能吗?

4

5 回答 5

10

滚动你自己的:

// 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
于 2009-08-31T02:31:01.017 回答
3

好吧,我不确定这是否可能,但请记住,每个 Objective-C 方法都有两个隐式/隐藏参数,self并且_cmd. AnIMP通常是typedef这样的:

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

如果你想 jerry-rig 方法和选择器,你需要一个看起来像这样的方法:

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

但即使在那之后,您还需要在运行时注册一个选择器名称,然后您需要将该选择器与方法相关联,但这是在逐个类的基础上完成的(即类可以有相同选择器的不同实现名称),所以你至少需要一个虚拟类。

NSThread仅仅创建一个虚拟类和该类的虚拟实例比调用各种运行时 API来调用单个 C 函数要简单得多。

于 2009-08-30T23:54:23.713 回答
3

使用简单调用该函数的方法创建一个 Objective-C 类。获取该方法的选择器并将其传递给NSThreadAPI。

于 2009-08-30T22:11:34.187 回答
1

如果你不需要NSThread东西,你也可以直接用 POSIX interface启动一个线程。

于 2009-08-31T10:26:50.743 回答
0

我想使用 C 函数而不是 Objective-C 方法启动一个新线程

那你为什么不直接使用:

  1. POSIX 线程,
  2. GCD?

dispatch_async_f() (man)正好适合这个目的。

于 2014-05-18T06:14:02.747 回答