web-dev-qa-db-fra.com

Un IBOutlet doit-il être une propriété et synthétisé?

Dans la plupart des exemples, je vois la configuration suivante des IBOutlets:



(Example A)

FooController.h:

@interface FooController : UIViewController {
    UILabel *fooLabel;
}

@property (nonatomic, retain) IBOutlet UILabel *fooLabel;

@end

FooController.m:

@implementation FooController

@synthesize fooLabel;

@end

Mais cela fonctionne aussi très bien (remarque: pas de propriété et pas de synthèse):



(Example B)

FooController.h:

@interface FooController : UIViewController {
    IBOutlet UILabel *fooLabel;
}

@end

FooController.m:

@implementation FooController

@end

Y a-t-il des inconvénients à définir des IBOutlets comme dans l'exemple B? Vous aimez les fuites de mémoire? Semble fonctionner correctement et je préfère ne pas exposer les IBOutlets en tant que propriétés publiques car ils ne sont pas utilisés en tant que tels, ils ne sont utilisés que dans la mise en œuvre du contrôleur. Le définir à trois endroits sans besoin réel ne me semble pas très DRY (Ne vous répétez pas).

53
Ward Bekker

Sur Mac OS X, les IBOutlets sont connectés comme suit:

  1. Recherchez une méthode appelée set <OutletName>:. S'il existe, appelez-le.
  2. Si aucune méthode n'existe, recherchez une variable d'instance nommée <OutletName>, définissez-la sans la conserver .

Sur iPhone OS, les IBOutlets sont connectés comme ceci:

  1. appeler [object setValue: outletValue forKey: @ "<OutletName>"]

Le comportement de la valeur définie pour la clé est de faire quelque chose comme ceci:

  1. Recherchez une méthode appelée set <OutletName>:. S'il existe, appelez-le.
  2. Si aucune méthode n'existe, recherchez une variable d'instance nommée <OutletName>, définissez-la et conservez-la .

Si vous utilisez une propriété, vous tomberez dans le cas " Recherchez une méthode appelée set <OutletName>: ..." sur les deux plates-formes. Si vous utilisez simplement une variable d'instance, vous aurez un comportement de conservation/libération différent sur Mac OS X VS iPhone OS. Il n'y a rien de mal à utiliser une variable d'instance, il vous suffit de faire face à cette différence de comportement lorsque vous passez d'une plate-forme à l'autre.

Voici un lien vers une documentation complète sur ce sujet uniquement. https://developer.Apple.com/library/archive/documentation/Cocoa/Conceptual/LoadingResources/CocoaNibs/CocoaNibs.html#//Apple_ref/doc/uid/10000051i-CH4-SW6

96
Jon Hess

Sous Mac OS X, les IBOutlets ne sont pas conservés par défaut. C'est le contraire du comportement sur iPhone OS: sur iPhone OS, si vous ne déclarez pas une propriété, elle est conservée et vous devez libérer cette propriété dans la méthode dealloc. De plus, le runtime 64 bits peut synthétiser des variables d'instance à l'aide de déclarations de propriétés. Cela signifie qu'un jour les variables d'instance (avec le IBOutlet) peuvent être omises.

Pour ces raisons, il est plus homogène et compatible de toujours créer une propriété et d'utiliser le IBOutlet uniquement dans la propriété. Malheureusement, il est également plus verbeux.

Dans votre premier exemple, vous devez toujours libérer la sortie dans la méthode dealloc. Dans votre deuxième exemple, vous devez libérer la prise uniquement avec iPhone OS.

12
Freeman

Le résultat final est exactement le même, mais vous devez garder quelques points à l'esprit:

  • Lorsque vous utilisez des champs d'instance comme sorties, vous ne devez PAS les libérer dans dealloc.

  • Lorsque vous utilisez des propriétés qui ont l'attribut (conserver), vous devez libérer la propriété dans dealloc (en utilisant self.property=nil ou en libérant la variable de support). Cela rend beaucoup plus transparent ce qui se passe.

En fait, tout se résume à la même vieille règle: " tu libéreras ce que tu alloues/retiens ". Donc, si vous utilisez un champ d'instance comme sortie, vous ne l'avez pas alloué/conservé, vous ne devez donc pas le libérer.

4
Philippe Leybaert

Il est possible que ces exemples utilisent la retenue car l'exemple de code alloue et initialise par programme un UILabel, puis l'ajoute à l'UIView. C'est le cas pour de nombreux exemples, car apprendre à utiliser Interface Builder n'est souvent pas leur but.

Le deuxième exemple (aucune propriété et aucune synthèse) avec l'IBOutlet est utilisé lorsque le développeur `` attribue '' l'UILabel (bouton, vue, etc.) dans le générateur d'interface - en faisant glisser l'IBOulet vers le composant Label ou autre composant View. À mon avis, l'action de glisser-déposer précédente (Étiquette sur la vue) ajoute également la sous-vue, l'étiquette à une vue - et ainsi de suite. L'étiquette est conservée par une vue; une vue est conservée par Window; La fenêtre est conservée par le propriétaire du fichier. Le propriétaire du fichier est généralement votre document qui est démarré en principal.

Vous remarquerez que lorsque vous parcourez votre programme (en ajoutant un awakeFromNib

- (void)awakeFromNib
{
    [fooLabel blahblah];
}

que fooLabel a déjà une adresse mémoire.

C'est parce que l'étiquette a été initialisée à partir d'un ensemble de fichiers (le fichier nib) en utilisant non pas init mais initWithCoder. Ce qui désérialise essentiellement le flux de fichiers en un objet - puis définit la variable IBOutlet. (Nous parlons toujours de la méthode IBOutlet).

Notez également que la méthode iOS susmentionnée utilise la méthode Key Value

  call [object setValue:outletValue forKey:@"<OutletName>"]

qui est le modèle Observateur/Observable. Ce modèle nécessite la référence d'objet observable à chaque observateur dans un ensemble/tableau. Un changement de valeur itérera le Set/Array et mettra également à jour tous les observateurs. Cet ensemble conservera déjà chaque observateur, d'où le manque de conservation dans iOS.

De plus, le reste n'est que spéculation.

Il semble que les cas où vous utilisez Interface Builder puis

 @property (nonatomic, retain) IBOutlet UILabel *fooLabel;

devrait éventuellement être changé en

@property (nonatomic, weak) IBOutlet UILabel *fooLabel;

ou @property (nonatomic, assign) IBOutlet UILabel * fooLabel;

Et puis il n'a pas besoin d'être publié dans une méthode de désallocation. De plus, il satisfera aux exigences OSX et iOS.

C'est basé sur la logique et je pourrais manquer quelques morceaux ici.

Néanmoins, peu importe si la vue persiste tout au long de la durée de votre programme. Alors qu'une étiquette dans une boîte de dialogue modale (ouvrir, fermer, ouvrir, fermer) peut en fait avoir une rétention excessive et une fuite par cycle. Et c'est parce que (spéculation à nouveau) chaque boîte de dialogue fermée est sérialisée dans un système de fichiers et persiste ainsi la position et la taille x, y, ainsi que ses sous-vues, etc. Et ensuite désérialisée ... lors de la prochaine session ouverte ou caché.)

1
Gabe Rainbow