2009-05-29 15 views
5

Recientemente estuve dando vueltas con una aplicación de muestra que intentaba poner mi cabeza completamente alrededor de NSRunLoop. La muestra que escribí creó un hilo secundario simple a través de NSOperation. El hilo secundario realiza algunas tareas, como procesar un NSTimer y también una transmisión rudimentaria con NSStream. Ambas fuentes de entrada requieren un NSRunLoop correctamente configurado para poder ejecutar.NSRunLoop del subproceso principal al que se hace referencia en un subproceso secundario

Mi pregunta es esta. Originalmente tenía un código que se veía así en el hilo secundario:

NSRunLoop* myRunLoop = [NSRunLoop currentRunLoop]; 
self.connectTimer = [NSTimer scheduledTimerWithTimeInterval:connectTimeout 
                target:self 
                selector:@selector(connectionConnectedCheck:) 
                userInfo:nil 
                repeats:NO]; 

[myRunLoop addTimer:self.connectTimer forMode:NSDefaultRunLoopMode]; // added source here 
[myRunLoop run]; 

[NSStream getStreamsToHostNamed:relayHost port:relayPort inputStream:&inputStream outputStream:&outputStream]; 
if ((inputStream != nil) && (outputStream != nil)) 
{ 
    sendState = kSKPSMTPConnecting; 
    isSecure = NO; 

    [inputStream retain]; 
    [outputStream retain]; 

    [inputStream setDelegate:self]; 
    [outputStream setDelegate:self]; 

    [inputStream scheduleInRunLoop: myRunLoop //[NSRunLoop currentRunLoop] 
            forMode:NSRunLoopCommonModes]; 
    [outputStream scheduleInRunLoop: myRunLoop //[NSRunLoop currentRunLoop] 
            forMode:NSRunLoopCommonModes]; 

    [inputStream open]; 
    [outputStream open]; 

    self.inputString = [NSMutableString string]; 



    return YES; 
} 

Ahora, con el código anterior, los eventos nunca lo haría proceso. No en currentRunLoop. Hice algo terrible después, ya que era solo un ejercicio educativo, y lo modifiqué para ejecutarlo bajo NSRunLoopmainRunLoop. Funcionó como magia. Sin embargo, estoy casi seguro de que, dependiendo de mis hilos principales, el ciclo de ejecución en un hilo secundario está en 10 niveles diferentes de incorrecto.

Así que mi pregunta es en dos partes, y espero que esté bien.

  1. ¿Qué puede ir mal con el pequeño 'hackear' que aplica el fin de obtener el subproceso secundario para funcionar y responder a eventos a través del bucle de ejecución?

  2. ¿Cuál es la forma correcta de configurar el subproceso secundario para escuchar a todos los eventos/fuentes basadas en temporizador, así que no tienen que hacer el paso 1.

Gracias por toda la visión.

Respuesta

6

Responder a sus preguntas en el orden inverso:

2. Tienes dos problemas. La documentación para -[NSRunLoop run] dice:

si no hay fuentes de entrada o temporizadores son unido al bucle de ejecución, este método sale inmediatamente; de lo contrario, ejecuta el receptor en el NSDefaultRunLoopMode por repetidamente invocando runMode:beforeDate:. En otras palabras , este método comienza efectivamente un bucle infinito que procesa los datos de las fuentes de entrada del bucle de ejecución y temporizadores .

Por lo tanto, utilizando el propio ciclo de ejecución de su hilo, probablemente no haya fuentes de entrada definidas para el ciclo de ejecución, por lo que está volviendo inmediatamente. Si los hay, su bucle de ejecución está girando infinitamente, y el resto de su código después de ese punto nunca se está ejecutando.

Para que todo funcione correctamente, su bucle de ejecución necesita primero tener algunas fuentes de entrada, y luego debe ejecutarse periódicamente para verificar si hay eventos. Tenga en cuenta que no desea usar [NSRunLoop run], ya que nunca podrá recuperar el control.En su lugar, recomendaría configurar un ciclo justo antes de return YES que ejecute continuamente el ciclo de ejecución hasta que se haya cancelado el hilo, o hasta que haya terminado de transmitir datos. Eso permitirá que el ciclo de ejecución procese los datos a medida que lleguen. Algo como esto:

while (!done) { 
    [myRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]]; 
} 

[NSRunLoop runUntilDate:] procesa eventos hasta la fecha especificada y devuelve el control al programa para que pueda hacer lo que quiera.

1. Está funcionando porque el ciclo de ejecución de su subproceso principal es que se está ejecutando periódicamente, por lo que se están gestionando los eventos. Sin embargo, si su hilo principal alguna vez se bloqueara, sus datos dejarían de llegar. Esto podría ser particularmente malo si el hilo principal estaba esperando datos de su segundo hilo. También, NSRunLoop no es thread-safe:

Advertencia: La clase NSRunLoop es generalmente no se considera para ser thread-safe y sus métodos deben solamente ser llamados en el contexto de el hilo actual. Nunca debe intentar llamar a los métodos de un objeto NSRunLoop ejecutándose en un subproceso diferente , ya que al hacerlo podría causar resultados inesperados. (a partir de la documentación NSRunLoop.)

Guía de programación Threading de Apple tiene una sección titulada Run Loop Management, lo que explica todo esto en algún grado. No es el documento más claro que haya leído, pero si está trabajando con ejecutar bucles, es un buen lugar para comenzar.

2

¿Recordó ejecutar el runloop?

[[NSRunLoop currentLoop] run] 
+0

De hecho, lo he intentado. Sin suerte. El código solo se ejecutará si se ejecutó con [NSRunLoop mainRunLoop] –

1

En primer lugar, se supone que el método scheduledTimer... agrega automáticamente el temporizador al bucle de ejecución actual. Solo necesita usar el método addTimer: si creó el temporizador usando el inicializador initWithFireDate.... Dudo que agregar el temporizador dos veces sea un problema, pero es una posibilidad.

Se supone que el método run de NSRunLoop regresa hasta que no haya más orígenes de eventos o se salga explícitamente del ciclo. Esto significa que debe programar las fuentes de eventos antes de llamando al run. Mueva su llamada [myRunLoop run] al final de su muestra de código. (También, obviamente, eliminar la declaración return)

La conclusión es que si [NSRunLoop run] está regresando, entonces usted no tiene un origen de evento programado en él. Use el depurador para recorrer el código. Si ve que avanza después de la llamada [NSRunLoop run], puede estar seguro de que tiene un problema con sus fuentes de entrada.

Cuestiones relacionadas