2009-08-30 7 views
6

Voglio iniziare una nuova discussione usando una funzione C, non un metodo C-obiettivo. Ho provatoÈ possibile utilizzare una funzione C come selettore in Cocoa?

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

dove ho

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

e data è un void *, ma ho un incidente di esecuzione in objc_msgSend, chiamato da

-[NSThread initWithTarget:selector:object:] 

Che cosa posso fare, invece? È possibile?

risposta

10

rotolare il proprio:

// 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 

NOTA: Ho scritto il codice di cui sopra e qui per luogo è di dominio pubblico. (A volte gli avvocati amano questo genere di cose) È anche completamente non testato!

È sempre possibile rimuovere le NSAutoreleasePool bit se si può garantire che la funzione di entrata thread crea anche uno ... ma è innocuo, non ha alcuna penalità velocità che-così-mai, e fa chiamare funzioni arbitrarie C più di tanto più semplice. Direi di tenerlo lì.

E lo si può utilizzare in questo modo:

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]; 
} 

Quando viene eseguito:

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 
+1

+1 Le categorie Obj-C sono molto utili a volte. – u0b34a0f6ae

+1

Buona idea, grazie! –

+0

Non c'è molto senso fare tutto questo e lanciare la propria implementazione, mentre possiamo haz 'dispathc_async_f()'. Probabilmente è meglio lasciare l'implementazione del threading agli ingegneri Apple. Forse sanno un po 'meglio cosa sta succedendo nel loro sistema. –

3

Creare una classe Objective-C con un metodo che chiama semplicemente tale funzione. Prendi il selettore di quel metodo e passalo all'API NSThread.

+0

OK, si, voglio dire, senza farlo. In altre parole, voglio sapere se mi manca qualche sintassi –

+1

Nessun NSThread chiama un metodo in modo da non avere scelta. – Mark

+5

Non penso che ci sia un semplice modo sintattico per farlo. Un selettore viene utilizzato per inviare un messaggio a un oggetto. L'invio di un messaggio non è identico al semplice richiamo di una funzione. I meccanismi sottostanti sono un po 'diversi. –

3

Beh, non sono sicuro se è possibile, ma di tenere presente che ogni metodo Objective-C ha due argomenti impliciti/nascosti, self e _cmd. Un IMP è di solito typedef 'D Ti piace questa:

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

Se desideri metodi jerry-rig e selettori, è necessario disporre di un metodo che sembra che:

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

Ma anche dopo che , è necessario registrare un nome di selettore con il runtime, quindi è necessario associare quel selettore con il metodo, ma questo viene fatto su una base classe per classe (cioè le classi possono avere diverse implementazioni dello stesso nome di selettore), quindi hai almeno bisogno di avere una lezione finta.

È molto, molto più semplice solo creare una classe fittizia e un'istanza fittizia di quella classe piuttosto che chiamare le varie API di runtime solo per ottenere NSThread per richiamare una singola funzione C.

0

Voglio iniziare un nuovo thread utilizzando una funzione C, non è un metodo oggettivo-C

Allora perché non basta usare:

  1. thread POSIX,
  2. GCD?

dispatch_async_f()(man) è adatto proprio per questo scopo.