I want to start a new thread using a C function, not an objective-C method. I tried
[NSThread detachNewThreadSelector: @selector(func) toTarget: nil withObject: id(data)];
where I have
void func(void *data) {
// ...
}
and data
is a void *
, but I get a runtime crash in objc_msgSend
, called from
-[NSThread initWithTarget:selector:object:]
What can I do instead? Is it even possible?
Roll your own:
// 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
NOTE: I wrote the above code and here by place it in the public domain. (sometimes the lawyers like this kind of stuff) It is also completely untested!
You can always remove the NSAutoreleasePool
bits if you can guarantee that the thread entry function also creates one... but it's harmless, has no speed penalty what-so-ever, and makes calling arbitrary C functions that much more simpler. I'd say just keep it there.
And you can use it like so:
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];
}
When run:
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
Create an Objective-C class with a method that simply calls that function. Take the selector of that method and pass it to NSThread
API.
Well, I'm not sure if it's possible, but keep in mind that every Objective-C method has two implicit/hidden arguments, self
and _cmd
. An IMP
is usually typedef
'd like this:
typedef id (*IMP)(id,SEL,...);
If you want to jerry-rig methods and selectors, you need to have a method that looks like that:
void func (id self, SEL _cmd, void *firstParameter);
But even after that, you need to register a selector name with the runtime, then you need to associate that selector with the method, but this is done on a class-by-class basis (i.e. classes can have different implementations of the same selector name), so you at least need to have a dummy class.
It is much, much simpler just to create a dummy class and dummy instance of that class than call the various runtime API just to get NSThread
to invoke a single C function.
If you don't need NSThread
stuff, you can also start a thread with direct POSIX interface.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With