web-dev-qa-db-fra.com

Les IBOutlets doivent-ils être forts ou faibles sous ARC?

Je développe exclusivement pour iOS 5 en utilisant ARC. Est-ce que IBOutlets à UIViews (et sous-classes) doit être strong ou weak?

Le suivant:

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

Se débarrasserait de tout cela:

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

Y at-il des problèmes pour ce faire? Les modèles utilisent strong de même que les propriétés générées automatiquement lors de la connexion directe à l'en-tête à partir de l'éditeur 'Interface Builder', mais pourquoi? La UIViewController a déjà une référence strong à sa view qui conserve ses sous-vues.

535
hypercrypt

La meilleure pratique actuellement recommandée par Apple est que les IBOutlets soient forts sauf si faibles est spécifiquement nécessaire pour éviter un cycle de conservation. Comme Johannes l'a mentionné ci-dessus, cela a été commenté dans la session "Implémentation de conceptions d'interface utilisateur dans Interface Builder" de WWDC 2015, où un Apple ingénieur a déclaré:

Et la dernière option que je veux souligner est le type de stockage, qui peut être fort ou faible. En général, vous devriez rendre votre sortie forte, en particulier si vous connectez une sortie à une sous-vue ou à une contrainte qui ne sera pas toujours conservée par la hiérarchie des vues. La seule fois où vous devez réellement affaiblir un point de vente, c’est si vous avez une vue personnalisée référençant quelque chose de nouveau dans la hiérarchie des vues, ce qui en général n’est pas recommandé.

J'ai posé cette question sur Twitter à un ingénieur de l'équipe de l'IB qui a confirmé que fort devrait être la valeur par défaut et que les documents de développement sont en cours de mise à jour.

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

229
Daniel Hall

AVERTISSEMENT, RÉPONSE VIDÉO : cette réponse n'est pas à jour au WWDC 2015, pour la réponse correcte, reportez-vous à la réponse acceptée = (Daniel Hall) ci-dessus. Cette réponse restera pour l'enregistrement.


Résumé de la bibliothèque de développeur :

D'un point de vue pratique, sous iOS et OS X, les points de vente doivent être définis en tant que propriétés déclarées. Les points de vente doivent généralement être faibles, sauf ceux qui vont de File’s Owner aux objets de niveau supérieur dans un fichier nib (ou, dans iOS, une scène de storyboard) qui doit être fort. Les prises que vous créez seront donc généralement faibles par défaut, car:

  • Les prises que vous créez, par exemple, les sous-vues de la vue d’un contrôleur de vue ou d’une fenêtre du contrôleur de fenêtre, sont des références arbitraires entre des objets qui n’impliquent pas la propriété.

  • Les points de vente forts sont souvent spécifiés par les classes de structure (par exemple, le point de vente de la vue UIViewController ou le point de vente de la fenêtre NSWindowController).

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

Bien que la documentation recommande d'utiliser weak sur les propriétés des sous-vues, depuis iOS 6, il semble préférable d'utiliser plutôt strong (le qualificatif de propriété par défaut). Cela est dû au changement dans UIViewController que les vues ne sont plus déchargées.

  • Avant iOS 6, si vous conserviez des liens étroits avec les sous-vues de la vue du contrôleur, si la vue principale du contrôleur de vue était déchargée, celles-ci resteraient en place tant que le contrôleur de vue est actif.
  • Depuis iOS 6, les vues ne sont plus déchargées, mais chargées une fois, puis restent tant que leur contrôleur est là. Donc, les propriétés fortes n'auront aucune importance. Ils ne créeront pas non plus de cycles de référence forts, car ils indiquent le graphe de référence fort.

Cela dit, je suis déchiré entre utiliser

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

et

@property (nonatomic) IBOutlet UIButton *button;

sous iOS 6 et après:

  • Utiliser weak indique clairement que le contrôleur ne veut pas être propriétaire du bouton.

  • Mais omettre weak ne fait pas mal dans iOS 6 sans déchargement de vue et est plus court. Certains peuvent faire remarquer que c'est aussi plus rapide, mais je n'ai pas encore rencontré d'application trop lente en raison de weakIBOutlets.

  • Ne pas utiliser weak peut être perçu comme une erreur.

Conclusion: depuis iOS 6, nous ne pouvons plus nous tromper tant que nous n'utilisons pas le déchargement de vues. Il est temps de faire la fête. ;)

48
Tammo Freese

Je ne vois pas de problème avec ça. Avant ARC, j'ai toujours fabriqué mes IBOutlets assign, car ils sont déjà conservés par leurs superviews. Si vous leur donnez weak, vous ne devriez pas avoir à les supprimer dans viewDidUnload, comme vous le dites.

Une mise en garde: vous pouvez prendre en charge iOS 4.x dans un projet ARC, mais si vous le faites, vous ne pouvez pas utiliser weak, vous devez donc les rendre assign, auquel cas vous ' d veux toujours supprimer la référence dans viewDidUnload pour éviter un pointeur en suspens. Voici un exemple de bug de pointeur pendant que j'ai rencontré:

Un UIViewController a un UITextField pour le code Zip. Il utilise CLLocationManager pour inverser le géocodage de l'emplacement de l'utilisateur et définir le code postal. Voici le rappel du délégué:

-(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];
}

J'ai trouvé que si je rejetais cette vue au bon moment et que je ne me mettais pas à zéro. Zip dans viewDidUnload, le rappel du délégué pouvait générer une exception d'accès incorrect sur self.Zip.text.

34

IBOutlet devrait être fort, pour des raisons de performances. Voir Référence du Storyboard, Strong IBOutlet, Scene Dock dans iOS 9

Comme expliqué dans ce paragraphe, les sorties vers les sous-vues de la vue du contrôleur de vue peuvent être faibles, car ces sous-vues appartiennent déjà à l’objet de niveau supérieur du fichier nib. Cependant, lorsqu'un point de vente est défini comme un pointeur faible et que le pointeur est défini, ARC appelle la fonction d'exécution:

id objc_storeWeak(id *object, id value);

Cela ajoute le pointeur (objet) à une table en utilisant la valeur de l'objet comme clé. Cette table est appelée la table faible. ARC utilise cette table pour stocker tous les pointeurs faibles de votre application. Désormais, lorsque la valeur de l'objet est désallouée, ARC effectuera une itération sur la table faible et définira la référence faible sur nil. Sinon, ARC peut appeler:

void objc_destroyWeak(id * object)

L'objet est ensuite désenregistré et objc_destroyWeak appelle à nouveau:

objc_storeWeak(id *object, nil)

Cette comptabilité associée à une référence faible peut prendre 2 à 3 fois plus de temps que la publication d'une référence forte. Ainsi, une référence faible introduit une surcharge pour le moteur d'exécution que vous pouvez éviter en définissant simplement les points de vente comme forts.

À partir de Xcode 7, il suggère strong

Si vous regardez la WWDC 2015 session 407 Implémentation de conceptions d'interface utilisateur dans Interface Builder , cela suggère (transcription de http://asciiwwdc.com/2015/sessions/407 ).

Et la dernière option que je veux souligner est le type de stockage, qui peut être fort ou faible.

En général, vous devriez rendre votre sortie forte, en particulier si vous connectez une prise à une vue secondaire ou à une contrainte qui ne sera pas toujours conservée par la hiérarchie des vues.

La seule fois où vous devez réellement affaiblir un point de vente, c’est si vous avez une vue personnalisée référençant quelque chose de nouveau dans la hiérarchie des vues, ce qui en général n’est pas recommandé.

Je vais donc choisir fort et je vais cliquer sur Connecter qui générera mon point de vente.

21
onmyway133

Dans le développement iOS, le chargement NIB est un peu différent du développement Mac.

Dans le développement Mac, un IBOutlet est généralement une référence faible: si vous avez une sous-classe de NSViewController, seule la vue de niveau supérieur est conservée et lorsque vous libérez le contrôleur, toutes ses sous-vues et ses sorties sont automatiquement libérées.

UiViewController utilise le codage par valeur de clé pour définir les points de vente en utilisant des références fortes. Ainsi, lorsque vous désaffectez votre UIViewController, la vue de dessus sera automatiquement désallouée, mais vous devez également désallouer tous ses points de vente dans la méthode dealloc.

Dans ce billet du Big Nerd Ranch , ils abordent ce sujet et expliquent pourquoi utiliser une référence forte dans IBOutlet n’est pas un bon choix (même si cela est recommandé par Apple dans ce cas).

20
Giuseppe

Une chose que je tiens à souligner ici, c'est que, malgré ce que les ingénieurs de Apple ont déclaré dans leur propre vidéo WWDC 2015 ici:

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

Apple ne cesse de changer d’avis sur le sujet, ce qui nous indique qu’il n’existe pas de bonne réponse à cette question. Pour montrer que même les ingénieurs de Apple sont divisés sur ce sujet, jetez un coup d'œil au dernier exemple de code d'Apple: vous verrez que certaines personnes utilisent un faible, et d'autres non.

Cet exemple de paiement Apple utilise une valeur faible: https://developer.Apple.com/library/ios/samplecode/Emporium/Listings/Emporium_ProductTableViewController_Swift.html#//Apple_ref/doc/ uid/TP40016175-Emporium_ProductTableViewController_Swift-DontLinkElementID_8

Comme dans cet exemple d'image: https://developer.Apple.com/library/ios/samplecode/AVFoundationPiPPlayer/Listings/AVFoundationPiPPlayer_PlayerViewController_Swift.html#//Apple_ref/doc/uid/TP40016166-AVFoundationPiPPlayer_PlayerViewController_Swift-DontLinkElementID_4

Comme dans l'exemple 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

De même que l'exemple d'emplacement principal: https://developer.Apple.com/library/ios/samplecode/PotLoc/Listings/Potloc_PotlocViewController_Swift.html#//Apple_ref/doc/uid/TP40016176- Potloc_PotlocViewController_Swift-DontLinkElementID_6

Tout comme l'exemple de prévisualisation du contrôleur de vue: https://developer.Apple.com/library/ios/samplecode/ViewControllerPreviews -Projects_PreviewUsingDelegate_PreviewUsingDelegate_DetailViewController_Swift-DontLinkElementID_5

Comme le fait le cas avec HomeKit: -DontLinkElementID_23

Tous ces éléments sont entièrement mis à jour pour iOS 9 et utilisent tous des points de vente faibles. Nous apprenons de cela A. La question n’est pas aussi simple que certains le prétendent. B. Apple a changé d'avis à plusieurs reprises, et C. Vous pouvez utiliser ce qui vous rend heureux :)

Un merci spécial à Paul Hudson (auteur de www.hackingwithsift.com) pour les éclaircissements apportés et les références de cette réponse.

J'espère que cela clarifie un peu mieux le sujet!

Prends soin de toi.

16
syedfa

Depuis WWDC 2015, il y a une session sur Implémentation de conceptions d'interface utilisateur dans Interface Builder . Autour de 32 minutes, il dit que vous voulez toujours rendre votre @IBOutlet fort .

9
Johannes

Sachez que IBOutletCollection devrait être @property (strong, nonatomic).

6
landonandrey

Il semble que quelque chose a changé au fil des ans et maintenant Apple recommande d'utiliser fort en général. Les preuves de leur session WWDC sont dans session 407 - Implémentation des conceptions d'interface utilisateur dans Interface Builder et commence à 32h30. Ma note d'après ce qu'il dit est (presque, sinon exactement, en le citant):

  • Les connexions de sortie en général devraient être fortes, en particulier si nous connectons une sous-vue ou une contrainte qui n'est pas toujours conservée par la hiérarchie de la vue

  • Une connexion de sortie faible peut être nécessaire lors de la création de vues personnalisées contenant une référence à quelque chose de sauvegardé dans la hiérarchie des vues. En général, cela n'est pas recommandé .

Dans les autres domaines, il devrait être toujours actif dès lors que certaines de nos vues personnalisées ne créent pas de cycle de conservation avec certaines vues dans la hiérarchie des vues.

EDIT:

Certains peuvent poser la question. Le fait de le garder avec une référence forte ne crée-t-il pas un cycle de conservation en tant que contrôleur de vue racine et que la vue propriétaire conserve la référence? Ou pourquoi cela a-t-il changé? Je pense que la réponse est plus tôt dans cette présentation quand ils décrivent comment les nibs sont créés à partir de xib. Un nib distinct a été créé pour un VC et pour la vue. Je pense que cela pourrait être la raison pour laquelle ils modifient les recommandations. Il serait quand même agréable d’obtenir une explication plus détaillée de Apple.

5
Julian Król

Je pense que les informations les plus importantes sont les suivantes: Les éléments de xib sont automatiquement affichés dans des sous-vues. Subviews est NSArray. NSArray possède ses éléments. etc ont de forts indicateurs sur eux. Donc, dans la plupart des cas, vous ne voulez pas créer un autre pointeur fort (IBOutlet)

Et avec ARC, vous n'avez rien à faire dans viewDidUnload

4
kraag22