2010-04-06 15 views
18

Estoy tratando de usar un hilo separado para trabajar con alguna API.iPhone: cómo usar performSelector: onThread: withObject: waitUntilDone: method?

El problema es que no puedo usar el método performSelector:onThread:withObject:waitUntilDone: con un hilo que he instanciado para esto.

Mi código:

@interface MyObject : NSObject { 
    NSThread *_myThread; 
} 
@property(nonatomic, retain) NSThread *myThread; 
@end 

@implementation MyObject 
@synthesize myThread = _myThread; 
- (NSThread *)myThread { 
    if (_myThread == nil) { 
    NSThread *myThreadTemp = [[NSThread alloc] init]; 
    [myThreadTemp start]; 
    self. myThread = myThreadTemp; 
    [myThreadTemp release]; 
    } 
    return _myThread; 
} 

- (id)init { 
    if (self = [super init]) { 
    [self performSelector:@selector(privateInit:) onThread:[self myThread] withObject:nil waitUntilDone:NO]; 
    } 
    return self; 
} 
- (void)privateInit:(id)object { 
    NSLog(@"MyObject - privateInit start"); 
} 

- (void)dealloc { 
    [_myThread release]; 
    _myThread = nil; 
    [super dealloc]; 
} 
@end 

"MyObject - privateInit start" no se imprime.
¿Qué me estoy perdiendo?

Intenté crear una instancia del subproceso con el destino y el selector, intenté esperar la finalización de la ejecución del método (waitUntilDone:YES).
Nada ayuda.

ACTUALIZACIÓN:
No necesito este multiproceso para separar las operaciones costosas de otro hilo.
En este caso podría usar el performSelectorInBackground como se menciona en algunas respuestas.
La razón principal para este hilo separado es la necesidad de realizar todas las acciones en la API (TTS por Loquendo) desde un solo hilo.
Lo que significa que tengo que crear una instancia del objeto TTS y llamar a métodos en ese objeto desde el mismo hilo todo el tiempo.

Respuesta

14

¡He encontrado una respuesta!

Con el fin de mantener el hilo hacia arriba, hay una necesidad en la pieza adicional de código:

- (void)threadMain:(id)data { 
    NSAutoreleasePool *pool = [NSAutoreleasePool new]; 

    NSRunLoop *runloop = [NSRunLoop currentRunLoop]; 
    [runloop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode]; 

    while (isAlive) { // 'isAlive' is a variable that is used to control the thread existence... 
     [runloop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; 
    } 

    [pool release]; 
} 


Y la línea siguiente:

NSThread *myThreadTemp = [[NSThread alloc] init]; 

debe ser sustituida por la siguiente:

NSThread *myThreadTemp = [[NSThread alloc] initWithTarget:self selector:@selector(threadMain:) object:nil]; 

EDIT: Como fue sugerido por unos pocos aquí he agregado algunas líneas de código (NSAutoreleasePool, addPort method y 'isAlive' boolean).

+1

¿No están creando un bucle infinito de esa manera? Si el runloop no tiene nada que ver, simplemente se cerrará inmediatamente y todo lo que haces es llamar '- run' con la frecuencia que tu procesador pueda manejar. (Uso de la CPU al 100%) – bastibe

+0

Infinite - sí. Pero el ciclo no itera todo el tiempo. He puesto una línea de registro (NSLog ...) dentro del tiempo y solo se llamó una vez ... –

+0

Esto es un comportamiento inesperado y no se debe confiar en él. Un bucle de ejecución sin fuentes de entrada o temporizadores configurados debe salir inmediatamente, y por lo tanto, su bucle debe ejecutarse constantemente, una y otra vez, usando 100% de CPU, como decía el cartel. Consulte los documentos de nsrunloop para obtener más información. –

1

Ha creado el hilo, pero no está en ejecución. Debe ejecutarse para ejecutar algo.

También puede usar "performSelectorInBackground" en su lugar. Pondrá en cola la invocación hasta que finalice la inicialización.

+0

Gracias por su comentario. '[myThreadTemp start];' no se supone que hace que el subproceso se ejecute? 'performSelectorInBackground' no es bueno para mí. Tengo que usar un hilo para todas las operaciones en esa API que uso (TTS por Loquendo). –

+1

En el tiempo entre '[myThreadTemp start];' y su selector de rendimiento, el hilo puede haberse detenido. – Giao

+0

Si necesita un hilo separado, puede usar "detachNewThreadSelector: toTarget: withObject:". Creará y engendrará un nuevo hilo que comenzará inmediatamente, por lo que no tiene que preocuparse por la creación de NSThread. –

4

Bueno, supongo que tengo una solución mejor

- (void)run{ 
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
    NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; 
    running = true; 
    [[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode]; 
    while (running && [runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]){ 
     //run loop spinned ones 
    } 

    [pool release]; 
} 

¿Qué estoy haciendo aquí?
1) Agregar un puerto simulado aquí como Fuente evitará que el método runMode:beforeDate: salga de manera inmediata.
2) El método runMode:beforeDate: bloquea el hilo hasta que haya algo en runLoop.

+0

Si llamo a performSelector: onThread: ¿varias veces, hago toda la cola de selecciones? – onmyway133

12

Esto es lo que funciona para mí.bucle principal tomada de la documentación de Apple http://developer.apple.com/iphone/library/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html#//apple_ref/doc/uid/10000057i-CH16-SW25

- (void) start { 
    self.imageSaverThread = [[[NSThread alloc] initWithTarget:self selector:@selector(imageSaverThreadMain) object:nil] autorelease]; 
    [self.imageSaverThread start]; 
} 

- (void) imageSaverKeepAlive { 
    [self performSelector:@selector(imageSaverKeepAlive) withObject:nil afterDelay:60];  
} 

- (void)imageSaverThreadMain 
{ 
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 

    // Add selector to prevent CFRunLoopRunInMode from returning immediately 
    [self performSelector:@selector(imageSaverKeepAlive) withObject:nil afterDelay:60]; 
    BOOL done = NO; 

    do 
    { 
     NSAutoreleasePool *tempPool = [[NSAutoreleasePool alloc] init]; 
     // Start the run loop but return after each source is handled. 
     SInt32 result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, YES); 

     // If a source explicitly stopped the run loop, or if there are no 
     // sources or timers, go ahead and exit. 
     if ((result == kCFRunLoopRunStopped) || (result == kCFRunLoopRunFinished)) 
      done = YES; 

     [tempPool release]; 
    } 
    while (!done); 

    [pool release]; 
} 

Espero que ayuda

+0

Ahora esto es una ayuda. Observe cómo se debe agregar algún tipo de evento para evitar que el ciclo de ejecución vuelva inmediatamente. –

Cuestiones relacionadas