example ios macos memory-management memory-leaks nsurlsession

ios - swift url session example



NSURLSession memory leak (3)

Incluso después de invalidar una sesión de NSURLS, ejecutando un perfil utilizando instrumentos, algunas clases (probablemente privadas) llamaron TubeManager, HTTPConnectionCache y HTTPConnectionCacheDictionary a la memoria.

Fragmento de código para reproducir:

NSURLSessionConfiguration* config = [NSURLSessionConfiguration defaultSessionConfiguration]; NSURLSession* session = [NSURLSession sessionWithConfiguration:config]; NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.google.com"]]; NSURLSessionDataTask* sessionDataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { [session finishTasksAndInvalidate]; }]; [sessionDataTask resume];


¿Entonces cuál es la pregunta? ¿Quieres desactivar el almacenamiento en caché? Las respuestas de red se NSURLCache en caché rutinariamente en NSURLCache tanto en memoria como en almacenamiento persistente.

Si el uso de esta caché es problemático, cambie la requestCachePolicy para la configuración de la sesión en consecuencia. O cambie la cachePolicy de una NSMutableURLRequest , en sí misma. También puede configurar el tamaño máximo de URLCache que utiliza la configuración de la sesión, para restringir tanto la cantidad de uso de la memoria RAM como la cantidad de uso de memoria persistente.

Incluso si desactivas el almacenamiento en caché, como regla general, uno no debería sorprenderse de que las llamadas API aumenten el consumo de memoria, sin que sea tu culpa. No es raro que una aplicación experimente un consumo de memoria modesto la primera vez que realiza alguna tarea. Pero uno no debería ver el mismo crecimiento en iteraciones posteriores mientras se ejecuta la aplicación. Al realizar un seguimiento del uso de la memoria, generalmente es recomendable repetir la tarea varias veces y ver si la aplicación vuelve a un estado estable (lo que es deseable) o si continúa creciendo (lo que requiere una mayor investigación para asegurarse de que su código no sea fuente del problema). Pero rara vez nos preocupamos por el consumo inicial de memoria a menos que sea dramático.

En cuanto a su fragmento de código, obviamente no hay nada mal. Me inclinaría a sospechar el consumo de memoria de rutina por iOS. Suponiendo que el problema es más amplio que el comportamiento general de caché, si el consumo de memoria es dramático y / o continúa creciendo cada vez que la aplicación realiza este código, brinde más detalles y podemos ayudarlo a diagnosticar esto aún más.

Este es el aspecto de mi perfil de memoria después de cuatro lotes de 100 solicitudes cada uno; más una bandera final después de que emití una advertencia de memoria:

(Tenga en cuenta que se trata de una imagen compuesta para que pueda mostrar la memoria antes del primer lote, antes del tercer lote, después del último lote y después de que haya publicado manualmente la advertencia de memoria. Combiné estos elementos para que sea más fácil ver cuáles son las asignaciones totales estaban en esos cuatro puntos en el tiempo).


Tenga en cuenta que en iOS 9, el marco de seguridad asigna appx. 4k de datos de caché SSL y lo carga en su aplicación la primera vez que reanude una tarea para un nuevo objeto NSURLSession. Preguntas y respuestas técnicas de Apple QA1727 nos dice que este caché SSL persiste durante 10 minutos, sin importar qué, ya que es privado y está completamente administrado por el sistema (¡porque es la seguridad!).

En el ejemplo del código, está creando un nuevo objeto NSURLSession cada vez que realiza una solicitud. Pero solo está utilizando defaultSessionConfiguration y no especifica un delegado al que podría existir una referencia fuerte, entonces lo que debería hacer es simplemente usar el singleton [NSURLSession sharedSession] y usar resetWithCompletionHandler para borrar las asignaciones que no sean de seguridad. O bien, cree un singleton personalizado si desea personalizar la configuración.

  • (NSURLSession *) Discusión sharedSession

Para las solicitudes básicas, la clase de sesión de URL proporciona un objeto de sesión singleton compartido que le proporciona un comportamiento predeterminado razonable. Al usar la sesión compartida, puede buscar los contenidos de una URL en la memoria con solo unas pocas líneas de código.

A diferencia de los otros tipos de sesión, no crea la sesión compartida; simplemente lo solicita llamando a [NSURLSession sharedSession]. Como resultado, no proporciona un delegado o un objeto de configuración. Por lo tanto, con la sesión compartida:

- You cannot obtain data incrementally as it arrives from the server. - You cannot significantly customize the default connection behavior. - Your ability to perform authentication is limited. - You cannot perform background downloads or uploads while your app is not running.

La sesión compartida utiliza los objetos NSURLCache, NSHTTPCookieStorage y NSURLCredentialStorage compartidos, utiliza una lista de protocolo de red personalizada compartida (configurada con registerClass: y unregisterClass :) y se basa en una configuración predeterminada.

Cuando trabaje con una sesión compartida, generalmente debe evitar personalizar la caché, el almacenamiento de cookies o el almacenamiento de credenciales (a menos que ya lo esté haciendo con NSURLConnection), porque hay muchas posibilidades de que supere las capacidades de la sesión predeterminada. , momento en el que tendrá que volver a escribir todas esas personalizaciones de manera que funcione con sus sesiones de URL personalizadas.

En otras palabras, si está haciendo algo con cachés, cookies, autenticación o protocolos de red personalizados, probablemente debería estar utilizando una sesión predeterminada en lugar de la sesión predeterminada.

(De NSURLSession Class Reference ... Cursiva mía; P)

Si no está utilizando el sharedSession Apple, entonces debería al menos seguir el ejemplo de Apple y hacer rodar su singleton con una propiedad de sesión. El objetivo de una sesión es que está destinado a vivir más tiempo que una sola solicitud. A pesar de su documentación poco clara, el hecho de que Apple proporciona un singleton, y lo llama una "sesión", indica que los objetos de sesión estaban destinados a vivir más que una sola solicitud.

Sí, se supone que debe invalidateAndCancel y invalidateAndCancel en algún momento, pero no después de cada solicitud, y ni siquiera si cada solicitud va a un servidor diferente por completo (que casi nunca es el caso). Solo necesita invalidar y cancelar si va a interrumpir su referencia a una sesión en particular; de lo contrario, puede simplemente llamar a flushWithCompletionHandler o resetWithCompletionHandler en la sesión para vaciar las asignaciones de montón de su sesión a la VM, o reiniciar para borrar tanto el almacenamiento dinámico como el de VM. (También vea mi respuesta aquí ).


finishTasksAndInvalidate llamado en el lugar equivocado ... completionHandler es para manejar la respuesta no tiene nada que ver con la sesión

Aquí está el código correcto:

NSURLSessionConfiguration* config = [NSURLSessionConfigurationdefaultSessionConfiguration]; NSURLSession* session = [NSURLSession sessionWithConfiguration:config]; NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.google.com"]]; NSURLSessionDataTask* sessionDataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { // handle response... }]; [sessionDataTask resume]; [session finishTasksAndInvalidate];