[iPhone only] Proxy dans un xib

Philippe49Philippe49 Membre
décembre 2008 modifié dans API UIKit #1
Dans la programmation pour iPhone, on peut utiliser davantage les proxies dans les fichiers xib. entrée dans la doc (listing 2-6)

Pour la configuration d'un UIViewController par un nib présentant un (ou plusieurs) proxy(ies), on peut donc utiliser la méthode loadNibNamed: owner: options: qui renvoie une NSArray avec les objets racines du nib.

Maintenant, ce que je ne vois pas trop pour l'instant, c'est ce qui va remplacer le traditionnel :
MyViewController * theViewController=[[MyViewController alloc] initWithNibNamed:@... bundle:...]
  

Réponses

  • Philippe49Philippe49 Membre
    00:13 modifié #2
    C'était logiquement simple :

    MyViewController * theViewController=[[MyViewController alloc] init];
    ....   // voir listing 2-6
    ....
    NSArray * topLevelObjs=[[NSBundle mainBundle] loadNibNamed:@.... owner:theViewController options:options]
  • AliGatorAliGator Membre, Modérateur
    00:13 modifié #3
    J'avais déjà  vu cette possibilité et m'étais posé la question...

    Maintenant j'avais peur que "loadNibNamed" charge le NIB en mémoire tout de suite (même si le viewController n'est pas affiché/pushé/...), là  où initWithNibNamed stocke pour moi le nom du NIB à  charger quand il en n'a besoin, mais ne le charge qu'à  la demande seulement au moment où c'est nécessaire, le décharge plus tard si besoin, pour le recharger quand c'est re-nécessaire, etc...
    Alors que loadNibNamed effectue effectivement le chargement je pense... immédiatement, que ce soit nécessaire ou pas, non ?
  • Philippe49Philippe49 Membre
    00:13 modifié #4
    Extrait du Nib Object Cycle Life

    Je ne lis pas cela ?

    Nib Object Retention
    Each time you ask the NSBundle or NSNib class to load a nib file, the underlying code creates a new copy of the objects in that file and returns them to you. The nib-loading code does not recycle nib file objects from a previous load attempt.


    ....

    For both Mac OS X and UIKit, the recommended way to manage the top-level objects in a nib file is to create outlets for them in the File's Owner object and then define setter methods to retain and release those objects as needed. Setter methods give you an appropriate place to include your memory-management code, even in situations where your application uses garbage collection. One easy way to implement your setter methods is to use the @property syntax and let the compiler create them for you.


    iPhone OS - managed memory model
    Objects in the nib file are created with a retain count of 1 and then autoreleased. As it rebuilds the object hierarchy, however, UIKit reestablishes connections between the objects using the setValue:forKey: method, which uses the available setter method or retains the object by default if no setter method is available ...
    If you do not store the top-level objects in outlets, you must retain either the array returned by the loadNibNamed:owner:options: method or the objects inside the array to prevent those objects from being released prematurely.
  • Philippe49Philippe49 Membre
    décembre 2008 modifié #5
    Il y a effectivement des différences dans le cycle d'initialisation :

    • Avec un UIViewController présent dans un xib, le désarchivage semble retardé :

    Exemple : Le premier log affiche (null) , le second affiche l'outlet
    (mon modalViewController est dans le nib du view controller appelant la session modale)
    <br />	NSLog(@&quot;%@&quot;,modalViewController.unOutlet);<br />	[self presentModalViewController:modalViewController animated:YES];<br />	NSLog(@&quot;%@&quot;,modalViewController. unOutlet);<br />
    



    • Avec un UIViewController présent dans un xib, initWithNibName: n'est pas appelé


    Expérience : pas de log pour ce code , où le view controller est référencé dans un autre nib
    - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {<br />	NSLog(@&quot;initializing&quot;);<br />	if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {<br />		// Custom initialization<br />	}<br />	return self;<br />}
    



    • Avec loadNibNamed: owner: options:
    le chargement a l'air immédiat, on a immédiatement accès aux outlets.
  • AliGatorAliGator Membre, Modérateur
    00:13 modifié #6
    Ben oui, c'est ce que j'avais cru comprendre de la doc.
    Quand tu lis l'article dans la doc Apple sur les ViewControllers, ils te disent que grace à  initWithNibNamed c'est le viewController qui se démerde pour charger le NIB tout seul, et surtout seulement au moment où c'est nécessaire pour économiser de la mémoire. Et il te dit aussi que le ViewControlleur sait décharger le NIB s'il a besoin de mémoire et que ledit NIB ne sert plus pour l'instant, quitte à  le recharger ensuite plus tard s'il est de nouveau nécessaire.

    Quand tu vois une méthode comme "loadNibNamed", vu comment Apple est plutôt propre côté conventions de nommage, on sent que ça va par contre vraiment charger le NIB dès que tu appelles cette méthode, donc immédiatement, même si tu ne te sers pas de ce NIB tout de suite (genre dans ton cas si tu n'appelles pas "presentAsModalViewController")...
    Et je me demande donc même si ce n'est pas à  toi de décharger ce NIB aussi quand tu le charges à  la main (là  où dans le alloc/initWithNibNamed, le release sur topLevelObjects est fait j'imagine bien dans le dealloc de UIViewController)... Ce qui serait logique... mais veut aussi dire que c'est à  toi aussi de gérer les cas où tu as une alerte mémoire, pour vérifier si le NIB est toujours utile ou pas, et le décharger ou pas en conséquence, et le recharger plus tard, etc... ce que le UIViewController fait tout seul quand tu utilises initWithNibNamed, mais pas quand tu fais loadNibNamed !!
  • Philippe49Philippe49 Membre
    décembre 2008 modifié #7
    Le topLevelsObjects est livré en auto-release, et tout est repris à  chaque fois;
    J'ai essayé mon code avec Instruments/Leaks en supprimant le contenu de ma NSMutableArray pages qui catalogue les pageVC successifs, pas de fuite mémoire. C'est donc bien que les objets ne sont pas retenus par topLevelsObjects.
    D'ailleurs si je mets [topLevelsObjects release] à  la fin du code qui suit, il y a une erreur.

    Leur avertissement va plutôt dans ce sens : si vous n'avez pas fait de retain via une property(retain) l'objet est perdu.

    <br />	PageViewController* pageVC=[[PageViewController alloc] init];<br />	NSArray*&nbsp; &nbsp; topLevelObjs = nil;<br />	NSDictionary*&nbsp; &nbsp; proxies = [NSDictionary dictionaryWithObject:self.parentViewController forKey:@&quot;RootView&quot;];<br />	NSDictionary*&nbsp; &nbsp; options = [NSDictionary dictionaryWithObject:proxies forKey:UINibProxiedObjectsKey];<br />	<br />	topLevelObjs = [[NSBundle mainBundle] loadNibNamed:@&quot;PageView&quot; owner:pageVC options:options];<br />	[pages replaceObjectAtIndex:number withObject:pageVC];<br />	[pageVC release];
    

  • AliGatorAliGator Membre, Modérateur
    00:13 modifié #8
    OK pour le coup du retain, en plus c'est tout à  fait normal que ce soit autoreleased en fait.
    Par contre pour le coup que le loadNibNamed fait un chargement immédiat et le initWithNibNamed charge et décharge juste aux bons moments où c'est nécessaire... ce serait quand même bien de trouver un équivalent de initWithNibNamed qui puisse prendre les options qui vont bien et le dictionnaire pour les NSProxy objects.

    Pour l'instant loadNibNamed est le seul moyen que je vois pour pouvoir fournir ce dictionnaire d'objets proxys, mais comme il charge le NIB instantanément et non pas juste qd nécessaire (et surtout avec gestion de la mémoire comme il faut, ce qui est super important sur iPhone !) moi je suis pas fan de cette solution :P Ou alors faudrait réimplémenter le même principe que ce que font les ViewControllers côté gestion mémoire mais c'est p'tet pas si simple...
  • Philippe49Philippe49 Membre
    00:13 modifié #9
    dans 1230663307:

    OK pour le coup du retain, en plus c'est tout à  fait normal que ce soit autoreleased en fait.
    Par contre pour le coup que le loadNibNamed fait un chargement immédiat et le initWithNibNamed charge juste aux bons moments où c'est nécessaire

    Cela je vois bien, et l'exemple cité ci-dessus le montre bien tout en faisant apparaà®tre les pièges, mais après tout dans le code on peut très bien en faire autant avec loadNibNamed: ...

    dans 1230663307:

    et décharge juste aux bons moments où c'est nécessaire...

    Par contre, là , je ne vois pas comment cela est possible. Cela signifierait que lorsqu'on perd de vue une interface pendant quelque temps, on peut la retrouver réinitialisée aux données du nib ?
    Enfin, on va aller bouquiner un peu sur les UIViewController ...


  • AliGatorAliGator Membre, Modérateur
    décembre 2008 modifié #10
    dans 1230665963:

    Par contre, là , je ne vois pas comment cela est possible. Cela signifierait que lorsqu'on perd de vue une interface pendant quelque temps, on peut la retrouver réinitialisée aux données du nib ?
    Enfin, on va aller bouquiner un peu sur les UIViewController ...
    Ah ben c'est une bonne remarque, j'en sais fichtre rien ^^ mais en même temps quand tu quitte une application iPhone puis reviens dessus et qu'elle se retrouve dans exactement le même état, c'est pas le même genre de cas ?

    Voilà  en tout cas ce que dit la doc : ce qui veut dire que les ViewControllers savent décharger leur vue quand il n'y a pas assez de mémoire et que le NIB n'est plus utilisé... mais faut prévoir dans ce cas ce qu'il faut pour la recharger dans loadView (et sauver l'état dans didReceiveMemoryWarning avant du coup)
    didReceiveMemoryWarning
    Sent to the view controller when the application receives a memory warning.

    [tt]- (void)didReceiveMemoryWarning[/tt]

    Discussion
    The default implementation of this method determines whether the view controller implements loadView. If the view controller implements this method and its view is not displayed, its view is released. The next time the view is needed, the loadView method is invoked to recreate the view. Subclasses should override the loadView method to create the view.
  • Philippe49Philippe49 Membre
    décembre 2008 modifié #11
    • Effectivement, loadNibNamed: owner: options: ne renseigne pas la property nibName du view controller. La reconstruction de l'arborescence de la vue est donc impossible. Dans ce cas, faire un pageVC.nibName=@PageView; semble le minimum à  faire pour permettre le mécanisme de déchargement de la vue. Maintenant je ne vois encore rien permettant d'affirmer que cela est suffisant.

    • J'ai retrouvé une partie de la source de ce que tu dis sur le mécanisme de déchargement 
    View Controller Programming Guide for iPhone OS > Using View Controller
    Rien n'est dit sur loadNibNamed:
  • Philippe49Philippe49 Membre
    00:13 modifié #12
    dans 1230714664:

    Dans ce cas, faire un pageVC.nibName=@PageView; semble le minimum à  faire pour permettre le mécanisme de déchargement de la vue. Maintenant je ne vois encore rien permettant d'affirmer que cela est suffisant.

    Ceci dit, quand on installe un view controller dans un nib, le seul renseignement que l'on donne c'est le nom du nib qui l'initialise ... et il s'initialise tout seul à  partir de ce renseignement.
Connectez-vous ou Inscrivez-vous pour répondre.