Identificadores de tarea NSURLSessionDataTask no únicos

Tengo una aplicación de iOS que utiliza NSOperationQueue, NSOperations y AFNetworking 2.1.0 para desactivar las requestes en un server. El método -[NSOperation main] tiene el siguiente aspecto:

 - (void)main { AFHTTPSessionManager *sessionManager = [AFHTTPSessionManager shanetworkingSessionManager]; [sessionManager GET:@"url" parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) { NSLog(@"Success"); } failure:^(NSURLSessionDataTask *task, NSError *error) { NSLog(@"Failure"); } ]; } 

He notado que, de vez en cuando, las devoluciones de llamada para una operación en particular nunca se ejecutan, cuando se crean múltiples operaciones y se agregan al NSOperationQueue en rápida sucesión. Me sumergí en AFNetworking para tratar de entender por qué. Terminé en -[AFURLSessionManager dataTaskWithRequest:completionHandler] , que se ve así:

 - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler { NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:request]; AFURLSessionManagerTaskDelegate *delegate = [AFURLSessionManagerTaskDelegate delegateForManager:self completionHandler:completionHandler]; [self setDelegate:delegate forTask:dataTask]; return dataTask; } 

Agregué una statement de logging justo después de crear DataTask:

 NSLog(@"Task with id %@ created for %@ on queue %@", @(dataTask.taskIdentifier), request.URL.path, dispatch_get_current_queue()); 

El logging revela el problema:

 2014-02-26 14:11:25.071 App[50094:6a2f] Task with id 15 created for /url1 on queue <OS_dispatch_queue: NSOperationQueue 0xc4b8560[0xc4b8ac0]> 2014-02-26 14:11:25.071 App[50094:460f] Task with id 16 created for /url2 on queue <OS_dispatch_queue: NSOperationQueue 0xc4b8560[0xc4b8ac0]> 2014-02-26 14:11:26.274 App[50094:6a2f] Task with id 18 created for /url2 on queue <OS_dispatch_queue: NSOperationQueue 0xc4b8560[0xc4b8ac0]> 2014-02-26 14:11:26.274 App[50094:6c17] Task with id 17 created for /url1 on queue <OS_dispatch_queue: NSOperationQueue 0xc4b8560[0xc4b8ac0]> 2014-02-26 14:11:27.546 App[50094:6307] Task with id 20 created for /url2 on queue <OS_dispatch_queue: NSOperationQueue 0xc4b8560[0xc4b8ac0]> 2014-02-26 14:11:27.546 App[50094:6b17] Task with id 19 created for /url1 on queue <OS_dispatch_queue: NSOperationQueue 0xc4b8560[0xc4b8ac0]> 2014-02-26 14:11:28.705 App[50094:6b17] Task with id 21 created for /url1 on queue <OS_dispatch_queue: NSOperationQueue 0xc4b8560[0xc4b8ac0]> 2014-02-26 14:11:28.705 App[50094:6307] Task with id 21 created for /url2 on queue <OS_dispatch_queue: NSOperationQueue 0xc4b8560[0xc4b8ac0]> 2014-02-26 14:11:32.091 App[50094:6307] Task with id 22 created for /url2 on queue <OS_dispatch_queue: NSOperationQueue 0xc4b8560[0xc4b8ac0]> 2014-02-26 14:11:32.091 App[50094:6b17] Task with id 23 created for /url1 on queue <OS_dispatch_queue: NSOperationQueue 0xc4b8560[0xc4b8ac0]> 

Observe que el cuarto set en el logging tiene el mismo taskIdentifier que es lo que AFNetworking utiliza para asociar tareas con sus callbacks, a través de delegado.

Si obligo a NSOperations a ejecutarse en la queue principal, entonces no puedo volver a crear el problema: taskIdentifier siempre es único.

¿Alguien habia visto algo como esto antes? ¿Debo asegurarme de que -[NSURLSession dataTaskWithRequest:] ejecute solo en el subprocess principal para no get taskIdentifier colisiones taskIdentifier ?

No sé si esto sigue siendo relevante para ti o no, pero esta cosa exacta me hizo estremecerme toda la noche. Resulta que está respondiendo parcialmente el problema en su pregunta.

Como resultaría, si

 NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:request]; 

se ejecuta de forma asíncrona, entonces hay una pequeña posibilidad de que la instancia de NSURLSession asigne el mismo taskIdentifier a diferentes tareas.

Sin AFNetworking y sincronizar en todos los dataTaskWithRequest: methods, entonces, había dos maneras en que podía solucionar este problema.

Si no necesita el NSURLSessionTask regrese de este método, entonces la mejor manera sería hacer su propia queue de despacho y cualquier request que desee realizar con su administrador de session, simplemente lo envía a esa queue de forma asíncrona:

 static dispatch_queue_t my_queue; my_queue = dispatch_queue_create("MyQueueName", DISPATCH_QUEUE_CONCURRENT); // ... Later, that very same day dispatch_async(my_queue, ^{ // [sessionManager GET: ... }); 

El único problema con este método para su problema, sin embargo, era que todo parecía ejecutarse en la misma queue de operaciones, por lo que tal vez de esta manera no funcionaría en su caso. La otra manera (que es cómo lo hice) es realmente más simple. Simplemente sincronice en el AFURLSessionManager para que la invocación de dataTaskWithRequest: solo pueda ocurrir sincrónicamente así:

 @synchronized(sessionManager) { // [sessionManager GET: ... } 

O bien, sí, podría hacer las creaciones de tarea en el hilo principal. Pero para algunos proyectos no siempre es tan simple como eso.

NSURLSessionDataTask una NSURLSessionDataTask a su AFURLSessionManager lugar de intentar hacer directamente requestes GET. El administrador de session tiene la intención de NSURLSession la funcionalidad de NSURLSession .

Ejemplo:

 NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration]; NSURL *URL = [NSURL URLWithString:@"http://example.com/testapi.php"]; NSURLRequest *request = [NSURLRequest requestWithURL:URL]; NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:request completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) { if (error) { NSLog(@"error!"); } else { NSLog(@"task successful!"); } }]; [dataTask resume];