Pregunta ¿IBOutlets debería ser fuerte o débil bajo ARC?


Estoy desarrollando exclusivamente para iOS 5 usando ARC. Debería IBOutlets a UIViews (y subclases) ser strong o weak?

El seguimiento:

@property (nonatomic, weak) IBOutlet UIButton *button;

Me desharía de todo esto:

- (void)viewDidUnload
{
    // ...
    self.button = nil;
    // ...
}

¿Hay algún problema al hacer esto? Las plantillas están usando strong como son las propiedades generadas automáticamente cuando se conectan directamente al encabezado desde el editor de 'Interface Builder', pero ¿por qué? los UIViewController ya tiene una strong referencia a su view que conserva sus subvistas.


514
2017-10-06 17:56


origen


Respuestas:


La mejor práctica recomendada actual de Apple es que IBOutlets sea fuerte a no ser que débiles es específicamente necesario para evitar un ciclo de retención. Como mencionara anteriormente Johannes, esto se comentó en la sesión "Implementación de diseños de interfaz de usuario en Interface Builder" de WWDC 2015, donde un ingeniero de Apple dijo:

Y la última opción que quiero señalar es el tipo de almacenamiento, que puede   o sea fuerte o débil. En general, deberías hacer tu salida   fuerte, especialmente si está conectando un tomacorriente a una subvista o a   una restricción que no siempre será retenida por la vista   jerarquía. La única vez que realmente necesita debilitar una salida es si   tiene una vista personalizada que hace referencia a algo que respalda la vista   jerarquía y, en general, eso no es recomendable.

Le pregunté sobre esto en Twitter a un ingeniero del equipo del IB y confirmó que fuerte debería ser el predeterminado y que los documentos del desarrollador se están actualizando.

https://twitter.com/_danielhall/status/620716996326350848 https://twitter.com/_danielhall/status/620717252216623104


185
2017-07-14 00:59



ADVERTENCIA, RESPUESTA DESCONECTADA: esta respuesta no está actualizada según WWDC 2015, para la respuesta correcta, consulte el respuesta aceptada (Daniel Hall) arriba. Esta respuesta quedará para registro.


Resumido de la biblioteca de desarrolladores:

Desde una perspectiva práctica, en iOS y OS X las salidas deben definirse como propiedades declaradas. Por lo general, los puntos de venta deberían ser débiles, excepto aquellos desde el propietario del archivo hasta los objetos de nivel superior en un archivo de punta (o, en iOS, una escena del guión gráfico) que deberían ser fuertes. Por lo tanto, los puntos de venta que usted cree típicamente serán débiles por defecto, porque:

  • Los outlets que crea, por ejemplo, las subvistas de la vista de un controlador de vista o la ventana de un controlador de ventana, son referencias arbitrarias entre objetos que no implican propiedad.

  • Los puntos fuertes son frecuentemente especificados por las clases de framework (por ejemplo, UIViewController's view outlet, o NSWindowController's window outlet).

    @property (weak) IBOutlet MyView *viewContainerSubview;
    @property (strong) IBOutlet MyOtherClass *topLevelObject;
    

444
2017-10-11 16:10



Si bien la documentación recomienda el uso weak en propiedades para las subvistas, ya que iOS 6 parece estar bien para usar strong (el calificador de propiedad predeterminado) en su lugar. Eso es causado por el cambio en UIViewController que las vistas ya no se descargan

  • Antes de iOS 6, si mantenía vínculos fuertes con las subvistas de la vista del controlador, si la vista principal del controlador de vista se descargaba, esas se mantendrían en las subvistas siempre que el controlador de vista esté cerca.
  • Desde iOS 6, las vistas ya no se descargan, sino que se cargan una vez y luego se mantienen mientras el controlador esté allí. Las propiedades tan fuertes no importarán. Tampoco crearán fuertes ciclos de referencia, ya que apuntan hacia abajo el fuerte gráfico de referencia.

Dicho esto, estoy dividido entre el uso

@property (nonatomic, weak) IBOutlet UIButton *button;

y

@property (nonatomic) IBOutlet UIButton *button;

en iOS 6 y después:

  • Utilizando weak indica claramente que el controlador no quiere la propiedad del botón.

  • Pero omitiendo weak no duele en iOS 6 sin descarga de vista, y es más corto. Algunos pueden señalar que también es más rápido, pero todavía tengo que encontrar una aplicación que es demasiado lenta debido a weak  IBOutlets.

  • No usando weak puede ser percibido como un error.

En pocas palabras: desde iOS 6 no podemos seguir haciendo esto mal siempre que no usemos la vista de descarga. Tiempo de fiesta. ;)


46
2017-12-10 21:54



No veo ningún problema con eso. Pre-ARC, siempre he hecho mis IBOutlet assign, ya que ya están conservados por sus superviews. Si los haces weak, no debería tener que eliminarlos en viewDidUnload, como usted señala.

Una advertencia: puede admitir iOS 4.x en un proyecto ARC, pero si lo hace, no puede usar weak, entonces tendrías que hacerlos assign, en cuyo caso aún desea borrar la referencia en viewDidUnload para evitar un puntero colgando. Aquí hay un ejemplo de un error de puntero colgante que he experimentado:

Un UIViewController tiene un UITextField para el código postal. Utiliza CLLocationManager para revertir la geocodificación de la ubicación del usuario y establecer el código postal. Aquí está la devolución de llamada del delegado:

-(void)locationManager:(CLLocationManager *)manager
   didUpdateToLocation:(CLLocation *)newLocation
          fromLocation:(CLLocation *)oldLocation {
    Class geocoderClass = NSClassFromString(@"CLGeocoder");
    if (geocoderClass && IsEmpty(self.zip.text)) {
        id geocoder = [[geocoderClass alloc] init];
        [geocoder reverseGeocodeLocation:newLocation completionHandler:^(NSArray *placemarks, NSError *error) {
            if (self.zip && IsEmpty(self.zip.text)) {
                self.zip.text = [[placemarks objectAtIndex:0] postalCode];
            }
        }];    
    }
    [self.locationManager stopUpdatingLocation];
}

Encontré que si descartaba esta vista en el momento correcto y no me había autocomprimido. viewDidUnload, la devolución de llamada del delegado podría generar una excepción de acceso incorrecto en self.zip.text.


34
2017-10-07 22:34



En el desarrollo de iOS, la carga de NIB es un poco diferente del desarrollo de Mac.

En el desarrollo de Mac, un IBOutlet suele ser una referencia débil: si tiene una subclase de NSViewController, solo se conservará la vista de nivel superior y cuando desasigne el controlador, todas sus subvistas y salidas se liberarán automáticamente.

UiViewController usa Key Value Coding para establecer los puntos de venta usando referencias fuertes. Entonces, cuando desasigne su UIViewController, la vista superior se desasignará automáticamente, pero también debe desasignar todas sus salidas en el método dealloc.

En este post del Big Nerd Ranch, cubren este tema y también explican por qué usar una referencia fuerte en IBOutlet no es una buena opción (incluso si es recomendado por Apple en este caso).


20
2017-10-10 16:02



IBOutlet debe ser fuerte, por razones de rendimiento. Ver Storyboard Reference, Strong IBOutlet, Scene Dock en iOS 9

Como se explica en este párrafo, los puntos de venta de las subvistas de la vista   la vista del controlador puede ser débil, porque estas subvistas ya están   propiedad del objeto de nivel superior del archivo nib. Sin embargo, cuando un Outlet   se define como un puntero débil y el puntero se establece, ARC llama al   función de tiempo de ejecución:

id objc_storeWeak(id *object, id value); 

Esto agrega el puntero   (objeto) a una tabla usando el valor del objeto como una clave. Esta tabla es   referida como la tabla débil. ARC usa esta tabla para almacenar todo   indicadores débiles de su aplicación. Ahora, cuando el valor del objeto es   desasignado, ARC iterará sobre la tabla débil y establecerá el débil   referencia a nil. Alternativamente, ARC puede llamar:

void objc_destroyWeak(id * object)

Entonces, el objeto es   llamadas no registradas y objc_destroyWeak nuevamente:

objc_storeWeak(id *object, nil)

Esta contabilidad asociada   con una referencia débil puede tardar 2-3 veces más en el lanzamiento de un   fuerte referencia. Entonces, una referencia débil introduce una sobrecarga para el   tiempo de ejecución que puede evitar simplemente definiendo puntos de venta como fuertes.

A partir de Xcode 7, sugiere strong

Si miras la sesión 407 de WWDC 2015 Implementación de diseños de UI en Interface Builder, sugiere (transcripción de http://asciiwwdc.com/2015/sessions/407)

Y la última opción que quiero señalar es el tipo de almacenamiento, que puede ser fuerte o débil.

En general, debe fortalecer su salida, especialmente si está conectando una toma de corriente a una subvista o a una restricción que la jerarquía de vista no siempre retendrá.

La única vez que realmente necesita debilitar una salida es si tiene una vista personalizada que haga referencia a algo que respalde la jerarquía de vistas y, en general, eso no es recomendable.

Así que voy a elegir fuerte y voy a hacer clic en conectar que generará mi salida.


14
2017-11-04 04:27



Una cosa que deseo señalar aquí, y eso es, a pesar de lo que los ingenieros de Apple han declarado en su propio video WWDC 2015 aquí:

https://developer.apple.com/videos/play/wwdc2015/407/

Apple sigue cambiando de opinión sobre el tema, lo que nos dice que no hay una sola respuesta correcta para esta pregunta. Para mostrar que incluso los ingenieros de Apple están divididos en este tema, eche un vistazo a la más reciente de Apple código de muestra, y verá que algunas personas usan débil y otras no.

Este ejemplo de Apple Pay usa weak: https://developer.apple.com/library/ios/samplecode/Emporium/Listings/Emporium_ProductTableViewController_swift.html#//apple_ref/doc/uid/TP40016175-Emporium_ProductTableViewController_swift-DontLinkElementID_8

Como lo hace este ejemplo de imagen en imagen: https://developer.apple.com/library/ios/samplecode/AVFoundationPiPPlayer/Listings/AVFoundationPiPPlayer_PlayerViewController_swift.html#//apple_ref/doc/uid/TP40016166-AVFoundationPiPPlayer_PlayerViewController_swift-DontLinkElementID_4

Como lo hace el ejemplo de Lister: https://developer.apple.com/library/ios/samplecode/Lister/Listings/Lister_ListCell_swift.html#//apple_ref/doc/uid/TP40014701-Lister_ListCell_swift-DontLinkElementID_57

Al igual que el ejemplo de Core Location: https://developer.apple.com/library/ios/samplecode/PotLoc/Listings/Potloc_PotlocViewController_swift.html#//apple_ref/doc/uid/TP40016176-Potloc_PotlocViewController_swift-DontLinkElementID_6

Como lo hace el ejemplo de vista previa del controlador de vista: https://developer.apple.com/library/ios/samplecode/ViewControllerPreviews/Listings/Projects_PreviewUsingDelegate_PreviewUsingDelegate_DetailViewController_swift.html#//apple_ref/doc/uid/TP40016546-Projects_PreviewUsingDelegate_PreviewUsingDelegate_DetailViewController_swift-DontLinkElementID_5

Como hace el ejemplo de HomeKit: https://developer.apple.com/library/ios/samplecode/HomeKitCatalog/Listings/HMCatalog_Homes_Action_Sets_ActionSetViewController_swift.html#//apple_ref/doc/uid/TP40015048-HMCatalog_Homes_Action_Sets_ActionSetViewController_swift-DontLinkElementID_23

Todos estos están completamente actualizados para iOS 9, y todos usan puntos débiles. De esto aprendemos que A. El problema no es tan simple como algunos creen. B. Apple ha cambiado de opinión varias veces, y C. Puedes usar lo que sea que te haga feliz :)

Un agradecimiento especial a Paul Hudson (autor de www.hackingwithsift.com) que me dio la aclaración y referencias para esta respuesta.

¡Espero que esto aclare el tema un poco mejor!

Cuídate.


11
2018-05-05 21:25