Condición de carrera en el código de ejemplo de Apple para Advanced NSOperations

TL: DR

¿Cuándo se establece el estado .Completed en NSURLSessionTask y cómo depende / afecta a completionHandler de la misma tarea?

¿Hay alguna forma de asegurarse de que el estado .Completed solo esté configurado después de que .Completed termine de ejecutarse?

Pregunta

Después de otra pregunta sobre aquí … Encadenar múltiples funciones asíncronas en Swift

Estaba apuntando en la dirección del código de ejemplo y charla WWDC de Advanced NSOperations.

Después de replicar parte del código en mi propio proyecto, he descubierto que parece que encuentro una condición de carrera que a veces funcionará y, a veces, fallará dependiendo de cómo se desarrolle la condición de carrera.

La operación que he creado es prácticamente una copy de DownloadEarthquakesOperation en el código de muestra.

Es una subclass de GroupOperation y contiene una URLSessionTaskOperation . El NSURLSessionTask se crea con un completionHandler NSURLSessionTask que procesa los datos descargados.

La class URLSessionTaskOperation funciona observando la propiedad de state de su tarea y luego termina la operación cuando se cambia a .Completed .

El problema que estoy encontrando es que parece que el state de la tarea se cambia a .Completed antes de que .Completed haya terminado de procesar.

El manejador de finalización que tengo es así …

 // this is a direct copy of the sample code just using data task let task = NSURLSession.shanetworkingSession().dataTaskWithRequest(request) { data, response, error in self.downloadFinished(data, response: response as? NSHTTPURLResponse, error: error) } 

La function que llama (en pseudo código) es algo así … (No tengo acceso al código exacto en este momento).

 func downloadFinished(data: NSData?, response: NSHTTPURLResponse?, error, NSError?) { // if the error is not nil then finish operation with error // if the response status code is not correct then finish with error // try to convert the data to a JSON object using NSJSONSerialization // finish with error if conversion failed // get a single Int value out of the JSON object // store the single Int value in an instance variable } 

No hay un código asynchronous aquí.

Después de que esto haya completado la operación de parte de esto (equivalente a GetEarthquakesOperation en el código de muestra) obtiene el valor de la variable de instancia y lo pasa a la siguiente operación.

El problema es que a veces este valor está ahí y, a veces, es nulo.

Al cerrar varias líneas en las diferentes classs puedo ver que la operación de networking se establece como completada en algún momento a lo largo de la ejecución del controller de finalización. Algunas veces antes de establecer el valor y algunas veces después.

Lo que es desconcertante es que he intentado obligar a que esto suceda en la aplicación de ejemplo, pero no pude. He intentado sleep en el manejador de finalización y he intentado poner un bucle de time de ejecución largo, pero ninguno de ellos parece funcionar.

¿Alguien puede ayudarme a tratar de arreglar esta condición de carrera?

Está bien, esto es extraño.

http://www.oliverfoggin.com/advanced-nsoperations-nsurlsessiondatatask-vs-nsurlsessiondownloadtask/

NSURLSessionDataTask y NSURLSessionDownloadTask ejecutan sus controlleres de finalización y establecen su estado completado de diferentes maneras.

La tarea de descarga solo se completa después de que el manejador de finalización haya terminado de ejecutarse.

La tarea de datos se establece como completada después de que el manejador de finalización ha comenzado a ejecutarse.

Esto está causando la condición de carrera en mi propio proyecto. Creo que cambiaré a usar la tarea de descarga por ahora.

También estaré archivando un radar.