NSURLSession Threads: seguimiento de múltiples descargas de background

Entonces estoy creando mi descarga en el hilo principal

NSURLRequest *request = [NSURLRequest requestWithURL:download.URL]; NSURLSessionDownloadTask *downloadTask = [self.downloadSession downloadTaskWithRequest:request]; [downloadTask resume]; 

y agregando el NSManagedContextID asociado con la descarga a un NSMutableDictionary, por lo que puedo recuperarlo más tarde en las rellamadas de los delegates

 [self.downloads setObject:[download objectID] forKey:[NSNumber numberWithInteger:downloadTask.taskIdentifier]]; 

Mi self.downloadSession arriba está configurado de esta manera

 - (NSURLSession *)backgroundSession { static NSURLSession *session = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfiguration:@"com.test.backgroundSession"]; configuration.discretionary = YES; session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil]; }); return session; } 

Mi problema es que las llamadas de delegado parecen llamarse en diferentes subprocesss.

 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite { NSManagedObjectID *downloadID = [self.downloads objectForKey:[NSNumber numberWithInteger:downloadTask.taskIdentifier]]; double progress = (double)totalBytesWritten / (double)totalBytesExpectedToWrite; NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:downloadID,@"download",[NSNumber numberWithDouble:progress],@"progress", nil]; [[NSNotificationCenter defaultCenter] postNotificationName:@"DownloadProgress" object:nil userInfo:userInfo]; } 

Entonces, cuando accedo a self.downloads para get el ID del object correcto, en realidad estoy accediendo al NSMutableDictionary desde un subprocess distinto al que se creó, y creo que NSMutableDictionary no es seguro para subprocesss. Entonces, ¿cuál es la mejor solución para esto, podría usar algo como esto?

 session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:[NSOperationQueue mainQueue]]; 

al declarar la session, establezca la queue delegada en el mainQueue que hace que todos los delegates sean llamados en el hilo principal, pero me gustaría mantener todas las devoluciones de llamada en un hilo de background si es posible

En su ejemplo, este no es un problema, ya que su dictionary se transfiere al sistema de notifications y el hilo de queue de operaciones no lo utiliza de nuevo. La security de subprocesss solo es un problema cuando se accede a un object desde varios subprocesss al mismo time.

Si su dictado fuera un iVar, debería hacerlo de esta manera:

Crea tu propia queue como esta

 myQueue = [[NSOperationQueue alloc] init]; // This creates basically a serial queue, since there is just on operation running at any time. [myQueue setMaxConcurrentOperationCount:1]; 

Luego programe cada acceso a su dictionary en esta queue, como por ejemplo:

 [myQueue addOperationWithBlock:^ { // Access your dictionary }]; 

Y, por supuesto, use esta queue para su delegación URLSesson:

 session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:myQueue]; 

Dado que esta queue está configurada como una queue de serie, siempre habrá un solo subprocess que acceda al dict en segundo plano.

Tenga cuidado cuando calcule algo con la información de dictado. Tienes que hacer esto en esa queue también. Sin embargo, puede poner el resultado de su cálculo en cualquier otra queue / subprocess, por ejemplo, para actualizar la interfaz de usuario en el subprocess principal.

 [myQueue addOperationWithBlock:^ { // Calculate with your dictionary // Maybe the progress calcualtion NSString* progress = [self calculateProgress: iVarDict]; dispatch_async(dispatch_get_main_queue(), ^ { // use progress to update UI }); }]; 

Creo que para publicar una notificación no tienes que usar ese patrón, porque el sistema maneja el subprocess correctamente. Pero para ser salvado debería verificar esto.

Puede usar una queue serie GCD para asegurarse de que solo un delegado se esté ejecutando simultáneamente.

Puede declarar la queue como una variable de instancia de su class e inicializarla en el método init, así:

 dispatch_queue_t delegateQueue; 

 delegateQueue = dispatch_queue_create("com.yourcompany.mydelegatequeue", 0x0); 

y en su método delegado, simplemente hágalo ejecutar en esta queue:

 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite { dispatch_sync(delegateQueue, ^{ NSManagedObjectID *downloadID = [self.downloads objectForKey:[NSNumber numberWithInteger:downloadTask.taskIdentifier]]; double progress = (double)totalBytesWritten / (double)totalBytesExpectedToWrite; NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:downloadID,@"download",[NSNumber numberWithDouble:progress],@"progress", nil]; [[NSNotificationCenter defaultCenter] postNotificationName:@"DownloadProgress" object:nil userInfo:userInfo]; }); } 

De esta manera, aunque se llama a cada delegado en su subprocess, solo de ellos se accede a self.downloads a la vez, y puede mantenerlos en subprocesss separados.