¿Establezco propiedades en nulo en dealloc cuando uso ARC?

125

Estoy tratando de aprender el conteo automático de referencias en iOS 5. Ahora, la primera parte de esta pregunta debería ser fácil:

  1. ¿Es correcto que NO necesito escribir declaraciones explícitas de propiedad de lanzamiento en mi dealloc cuando uso ARC? En otras palabras, ¿es cierto que lo siguiente NO necesita un acuerdo explícito?

    @interface MyClass : NSObject
    @property (strong, nonatomic) NSObject* myProperty;
    @end
    
    @implementation MyClass
    @synthesize myProperty;
    @end
  2. Mi siguiente y más importante pregunta proviene de una línea en el documento Notas de la versión de transición a ARC :

    No es necesario (de hecho no puede) liberar variables de instancia, pero es posible que deba invocar [self setDelegate: nil] en las clases del sistema y otro código que no se compila utilizando ARC.

    Esto plantea la pregunta: ¿cómo sé qué clases de sistema no están compiladas con ARC? ¿Cuándo debería crear mi propio Dealloc y establecer explícitamente propiedades de retención fuerte en cero? ¿Debo suponer que todas las clases de marco NS y UI utilizadas en las propiedades requieren deallocs explícitos?

Existe una gran cantidad de información sobre SO y en otros lugares sobre las prácticas de liberar el ivar de respaldo de una propiedad cuando se usa el seguimiento de referencia manual, pero relativamente poco acerca de esto cuando se usa ARC.

emfurry
fuente

Respuestas:

197

Respuesta corta : no, no tiene que anular propiedades en deallocARC.

Respuesta larga : nunca debe anular propiedades dealloc, incluso en la gestión manual de la memoria.

En MRR, debes liberar tus ivars . Anular las propiedades significa llamar a los establecedores, que pueden invocar código que no debería tocar dealloc(por ejemplo, si su clase, o una subclase, anula el establecedor). Del mismo modo, puede desencadenar notificaciones KVO. Al liberar el ivar, se evitan estos comportamientos no deseados.

En ARC, el sistema libera automáticamente cualquier ivars para usted, por lo que si eso es todo lo que está haciendo, ni siquiera tiene que implementarlo dealloc. Sin embargo, si tiene ivars no objeto que necesitan un manejo especial (por ejemplo, buffers asignados que necesita free()), aún tiene que lidiar con ellos dealloc.

Además, si se ha establecido como delegado de cualquier objeto, debe desestabilizar esa relación dealloc(esto es lo que se trata de llamar [obj setDelegate:nil]). La nota sobre hacer esto en clases que no están compiladas con ARC es un guiño a las propiedades débiles. Si la clase marca explícitamente su delegatepropiedad como weakentonces, no tiene que hacer esto, porque la naturaleza de las propiedades débiles significa que se anulará para usted. Sin embargo, si la propiedad está marcada assign, debe anularla en su dealloc, de lo contrario, la clase se quedará con un puntero colgante y probablemente se bloqueará si intenta enviar un mensaje a su delegado. Tenga en cuenta que esto solo se aplica a las relaciones no retenidas, como los delegados.

Lily Ballard
fuente
2
¡Esto tiene sentido! Sin embargo, permítame preguntarle esto: un escenario común que tengo es que tengo una MyController : UIViewControllerclase que crea y posee una UIView y también establece el delegado de la vista en sí mismo. Es el único propietario que retiene esa vista. Cuando el controlador se desasigna, la vista también debe desasignarse. ¿Importa entonces si el puntero delegado está colgando?
emfurry
44
@emfurry: Probablemente no, porque para cuando su controlador de vista muera, la vista en sí no debería estar en la jerarquía de vistas y no debería estar haciendo nada, pero es mejor no hacer suposiciones. ¿Qué sucede si la vista programada asincrónicamente se realizará más tarde, y la vista en sí misma termina superando su controlador de vista por un corto tiempo (por ejemplo, debido al trabajo asincrónico que retiene la vista temporalmente)? Lo mejor es ignorar al delegado para estar seguro. Y, de hecho, si la vista en cuestión es una UIWebView, los documentos declaran explícitamente que necesita anular el delegado.
Lily Ballard
3
@zeiteisen: No. unsafe_unretainedes exactamente equivalente a una assignpropiedad y es el comportamiento normal para las relaciones de delegado bajo MRR, y estos deben ser anulados.
Lily Ballard
44
No estoy de acuerdo con la declaración sobre no usar setters en dealloc con MRC. Apple no lo recomienda, pero también lo hacen en su código. En realidad, puede crear nuevos problemas al no usar el setter. Hay varias grandes discusiones al respecto. Lo importante es escribir el setter correctamente (debe comportarse correctamente si le pasa un valor nulo) y, a veces, observar el orden de desasignación.
Sulthan
77
@Sulthan: Si usar o no setters en dealloc es una gran lata de gusanos, pero mi posición básicamente se reduce a: quieres llamar a la menor cantidad de código posible en dealloc. Los setters tienden a incluir efectos secundarios, ya sea anulando en subclases, o mediante KVO u otros mecanismos. Los efectos secundarios en dealloc deben evitarse especialmente como la peste. Si posiblemente puede eliminar una llamada a un método de dealloc, debe hacerlo. Esto se simplifica a: no llame a los emisores en dealloc.
Lily Ballard
2

Solo para dar la respuesta opuesta ...

Respuesta corta : no, no tiene que anular las propiedades auto-sintetizadas en deallocARC. Y no tienes que usar el setter para los que están adentro init.

Respuesta larga : debe anular las propiedades sintetizadas personalizadas en dealloc, incluso bajo ARC. Y deberías usar el setter para aquellos en init.

El punto es que sus propiedades sintetizadas personalizadas deben ser seguras y simétricas con respecto a la anulación.

Un posible setter para un temporizador:

-(void)setTimer:(NSTimer *)timer
{
    if (timer == _timer)
        return;

    [timer retain];
    [_timer invalidate];
    [_timer release];
    _timer = timer;
    [_timer fire];
}

Un posible configurador para una vista de desplazamiento, vista de tabla, vista web, campo de texto, ...:

-(void)setScrollView:(UIScrollView *)scrollView
{
    if (scrollView == _scrollView)
        return;

    [scrollView retain];
    [_scrollView setDelegate:nil];
    [_scrollView release];
    _scrollView = scrollView;
    [_scrollView setDelegate:self];
}

Un setter posible para una propiedad KVO:

-(void)setButton:(UIButton *)button
{
    if (button == _button)
        return;

    [button retain];
    [_button removeObserver:self forKeyPath:@"tintColor"];
    [_button release];
    _button = button;
    [_button addObserver:self forKeyPath:@"tintColor" options:(NSKeyValueObservingOptions)0 context:NULL];
}

Entonces usted no tiene que duplicar ningún código para dealloc, didReceiveMemoryWarning, viewDidUnload, ... y su propiedad de manera segura puede hacerse pública. Si estaba preocupado por las propiedades nulas dealloc, entonces podría ser el momento de verificar nuevamente sus setters.

Coeur
fuente