¿Es necesario utilizar autoreleasepool en un programa Swift?

95

En la página 17 de esta presentación de WWDC14 , dice

¿Trabaja con Objective-C? Aún tengo que administrar grupos de
liberación automática autoreleasepool {/ * código * /}

Qué significa eso? ¿Significa que si mi código base no tiene ningún archivo Objective-C, autoreleasepool {}es innecesario?

En respuesta a una pregunta relacionada , hay un ejemplo en el que autoreleasepoolpuede resultar útil:

- (void)useALoadOfNumbers {
    for (int j = 0; j < 10000; ++j) {
        @autoreleasepool {
            for (int i = 0; i < 10000; ++i) {
                NSNumber *number = [NSNumber numberWithInt:(i+j)];
                NSLog(@"number = %p", number);
            }
        }
    }
}

Si el código anterior se traduce a Swift y se autoreleasepoolelimina, ¿Swift será lo suficientemente inteligente como para saber que la numbervariable debe publicarse después de la primera }(como lo hacen otros idiomas)?

Ethan
fuente
1
Parece que no hay documentación autoreleasepoolen Swift. Yo Ampliado de su pregunta y le pidió que en los foros dev .
Aaron Brager

Respuestas:

197

El autoreleasepoolpatrón se usa en Swift cuando se devuelven autoreleaseobjetos (creados por su código Objective-C o usando clases de Cocoa). El autoreleasepatrón en Swift funciona de manera muy similar a como lo hace en Objective-C. Por ejemplo, considere esta versión Swift de su método (instanciar NSImage/ UIImageobjetos):

func useManyImages() {
    let filename = pathForResourceInBundle

    for _ in 0 ..< 5 {
        autoreleasepool {
            for _ in 0 ..< 1000 {
                let image = NSImage(contentsOfFile: filename)
            }
        }
    }
}

Si ejecuta esto en Instrumentos, verá un gráfico de asignaciones como el siguiente:

con autoreleasepool

Pero si lo hace sin el grupo de liberación automática, verá que el uso máximo de memoria es mayor:

sin autoreleasepool

El le autoreleasepoolpermite administrar explícitamente cuándo se desasignan los objetos de liberación automática en Swift, al igual que pudo en Objective-C.

Nota: cuando se trata de objetos nativos de Swift, generalmente no recibirá objetos de liberación automática. Es por eso que la presentación mencionó la advertencia de que solo se necesita esto cuando se "trabaja con Objective-C", aunque me gustaría que Apple fuera más claro en este punto. Pero si se trata de objetos Objective-C (incluidas las clases Cocoa), pueden ser objetos de liberación automática, en cuyo caso esta versión Swift del @autoreleasepoolpatrón Objective-C sigue siendo útil.

Robar
fuente
2
En todas estas preguntas, usted puede escribir su propia clase, y tienen que hacer printlnen deinit, y se hace muy fácil verificar precisamente cuando se cancela la asignación objetos. O obsérvelo en Instrumentos. En respuesta a su pregunta, parece que los objetos Swift se devuelven desde funciones con +1 de retención de recuento (no objetos de liberación automática), y la persona que llama administrará sin problemas la propiedad desde ese punto (por ejemplo, si el objeto devuelto cae fuera del alcance y cuándo, se desasigna inmediatamente, no se coloca en un grupo de liberación automática).
Rob
3
@StevenHernandez Un grupo de liberación automática tiene muy poco que ver con las fugas. Una fuga es causada por un objeto inédito. El grupo de liberación automática, por otro lado, es solo una colección de objetos para los que la liberación se aplaza hasta que se agote el grupo. Los grupos no controlan si algo se desasigna o no, sino simplemente el momento de dicha desasignación. En la vista del mapa, no puede controlar qué almacenamiento en caché hace (usa memoria, pero no es una fuga verdadera) ni hacer nada si hubo una fuga real (y no estoy al tanto de ninguna fuga significativa en la vista del mapa, aunque históricamente ha habido fugas aleatorias y modestas en UIKit).
Rob
2
@matt Sí, vi un comportamiento similar. Así que repetí mi ejercicio con NSImage/ UIImageobjects y manifesté el problema de manera más consistente (y, francamente, este es un ejemplo más común del problema, ya que el uso máximo de memoria a menudo solo es problemático cuando se trata de objetos más grandes; un ejemplo práctico de esto podría ser una rutina para cambiar el tamaño de un montón de imágenes). También reproduje el comportamiento que llama al código Objective-C que crea explícitamente objetos de liberación automática. No me malinterpretes: creo que necesitamos grupos de liberación automática en Swift con menos frecuencia que en Objective-C, pero todavía tiene un papel que desempeñar.
Rob
1
¡Encontré un ejemplo que funciona! Simplemente llame a NSBundle pathForResource:ofType:repetidamente.
Matt
1
Mi pathForResource:ofType:ejemplo ya no funciona en Xcode 6.3 / Swift 1.2. :)
Matt
4

Si lo usara en el código Objective-C equivalente, lo usaría en Swift.

¿Swift será lo suficientemente inteligente como para saber que la variable numérica debe publicarse después de la primera}

Solo si Objective-C lo hace. Ambos operan según las reglas de administración de memoria de Cocoa.

Por supuesto, ARC sabe que eso numberqueda fuera de alcance al final de esa iteración del ciclo, y si lo retuvo, lo liberará allí. Sin embargo, eso no le dice si el objeto fue liberado automáticamente, ya -[NSNumber numberWithInt:] que puede haber devuelto o no una instancia liberada automáticamente. No hay forma de que pueda saberlo, porque no tiene acceso a la fuente de -[NSNumber numberWithInt:].

newacct
fuente
1
Si Swift se comporta igual que Objective-C para esto, ¿por qué la presentación mencionó "Trabajar con Objective-C?" ¿específicamente?
Ethan
9
@Ethan Parece que los objetos Swift nativos no son objetos de liberación automática y la autoreleasepoolconstrucción es completamente innecesaria. Pero si su código Swift está manejando objetos Objective-C (incluidos los objetos Cocoa), estos siguen patrones de liberación automática y, por lo tanto, la autoreleasepoolconstrucción se vuelve útil.
Rob
Entiendo que "El grupo de liberación automática le permite administrar explícitamente cuándo se desasignan los objetos de liberación automática en Swift", pero ¿por qué querría hacerlo? ¿Por qué el compilador no puede hacerlo por mí? Tuve que agregar mi propio grupo de liberación automática para evitar que la VM se disparara por el techo en un bucle cerrado de enormes manipulaciones de cuerdas. Para mí era obvio dónde debería agregarse, y funcionó perfectamente. ¿Por qué no pudo hacerlo el compilador? ¿Podría hacerse más inteligente el compilador para hacer un buen trabajo?
vonlost