Aspect séquentiel des bindings

Philippe49Philippe49 Membre
août 2008 modifié dans API AppKit #1
La vision "en gros" des bindings, c'est une synchronisation entre 2 ou plusieurs variables d'instances grâce à  un système de messages appelé le Key Value Observing (KVO) : Les possesseurs de ces variables reçoivent des messages d'observation leur indiquant la nécessité d'une mise à  jour. Mais évidemment, ce qui dans l'idéal devrait être une synchronisation instantanée, ne peut-être qu'une suite de mises à  jour dont l'ordre peut importer dans certaines situations. C'est ce post de Chacha qui motive cette étude.

L'essai est basé sur l'interface ci-dessous qui parle d'elle-même. Trois NSString sont en synchronisation, les deux dans les vues, et une en model dans AppController. 

Dans AppController, la chaà®ne se nomme textViewValue, et le setter la passe en majuscule :
@dynamic textViewValue;<br />-(NSString *) textViewValue {return textViewValue;}<br />-(void) setTextViewValue:(NSString *) value<br />{<br />	[textViewValue release];<br />	textViewValue=[[value&nbsp; uppercaseString] copy];<br />	BARATIN; // macro pour le message ajouté dans le label sur l&#39;interface<br />}

Ainsi, en cas de synchronisation parfaite, on devrait avoir des chaà®nes majuscules dans les deux vues. On va voir que ce n'est pas le cas.


Dans la sous-classe MyTextView créée pour l'occasion, on matérialise l'observation en rajoutant le code :
@implementation MyTextView<br />-(void) bind:(NSString*)binding toObject:(id)observableController withKeyPath:(NSString*)keyPath options:(NSDictionary*)options<br />{<br />	if ([binding isEqualToString:@&quot;value&quot;]){<br />		[observableController addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:NULL];<br />	}<br />	[super bind:binding toObject:observableController withKeyPath:keyPath options:options];<br />}<br /><br />- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context<br />{<br />	if([keyPath isEqualToString:@&quot;textViewValue&quot;])<br />		[appController addToMessage:@&quot;&#092;ntext view observer : text did change&quot;];<br />	// message ajouté dans le label sur l&#39;interface<br />}<br />

@end

Réponses

  • Philippe49Philippe49 Membre
    14:12 modifié #2
    On change le contenu du text view. Il est bindé à  textViewValue (Continuously Updates Value coché)  dans AppController. de mêm pour le TextField.

    Trajet probable de l'info :
    • On tape un caractère dans le textView
    • Cela effectue un changement sans doute dans [textView textStorage]
    • L'observation de cette chaà®ne provoque l'envoi d'un message setTextViewValue: à  AppController, et la mise en majuscule de la chaà®ne possédée par AppController.
    • Le setTextViewValue: provoque le mécanisme KVO, uniquement vers le textField, sans retour sur le textView, sinon on aurait des boucles infinies.
    • Le message setStringValue: est envoyé au textField avec la chaà®ne de AppController.

    Résultat :


  • Philippe49Philippe49 Membre
    14:12 modifié #3
    En partant du textField, c'est l'inverse.
    On remarque que le textView est passé entièrement en majuscule.
  • Philippe49Philippe49 Membre
    14:12 modifié #4
    En changeant directement dans AppController,le KVO agit maintenant en direction des deux vues, et on obtient bien ce que l'on veut.
  • Philippe49Philippe49 Membre
    14:12 modifié #5
    Si vous voulez essayer, améliorer, voici le code de l'appli
  • Philippe49Philippe49 Membre
    août 2008 modifié #6
    La solution pour synchroniser:
    ajouter dans le setter [textView setString:textViewValue] et idem pour le textField.
  • Philippe49Philippe49 Membre
    août 2008 modifié #7
    [size=12pt]L'aspect opaque :[/size]
    Pourquoi ajouter le textView en observateur, alors que le binding devrait normalement le faire ? Effectivement le binding ajoute des observateurs, mais cette observation semble réservée à  l'exécution du binding via un proxy. Il suffit de changer le code de la méthode de binding pour y inclure la lecture des observers de AppController
    -(void) bind:(NSString*)binding toObject:(id)observableController withKeyPath:(NSString*)keyPath options:(NSDictionary*)options
    {
    [super bind:binding toObject:observableController withKeyPath:keyPath options:options];
    NSLog(@Info pour AppController\n%@\;nTextView: %p",[appController observationInfo],self);
    }

    On réimplémente setString: pour simplement signaler le passage dans cette méthode
    -(void) setString:(NSString*) aString
    {
    NSLog(@Set string in textView);
    [super setString:aString];
    }


    [size=12pt]Expérience[/size]
    Le log signale deux observers≠TextView ou TextField et deux passages par setString: . Et donc aucun passage par la méthode observeValueForKeyPath: ofObject: change: context: du TextView.

    Info pour AppController
    <NSKeyValueObservationInfo 0x13f450> (
    <NSKeyValueObservance 0x14b5b0: Observer: 0x13e930, Key path: textViewValue, Options: <New: NO, Old: NO, Prior: NO> Context: 0x0, Property: 0x14b140>
    <NSKeyValueObservance 0x14d040: Observer: 0x14cc90, Key path: textViewValue, Options: <New: NO, Old: NO, Prior: NO> Context: 0x0, Property: 0x14b140>
    )
    TextView: 0x126cd0
    2008-08-08 12:41:03.035 Binding Interception[1110:10b] Set string in textView
    2008-08-08 12:41:03.037 Binding Interception[1110:10b] Set string in textView


    [size=12pt]Comment interpréter le mécanisme de retour (du textview vers appController)?[/size]
    [textView observationInfo] et [textField observationInfo] sont vides.

    [size=12pt]Que sont ces observateurs ?[/size]

    [EDIT] Un observer --> textView, Un observer --> textField
  • Philippe49Philippe49 Membre
    14:12 modifié #8
    dans 1218193226:

    [size=12pt]Que sont ces observateurs ?[/size]


    En recodant la méthode de binding, on observe les objets NSKeyValueObservance
    -(void) bind:(NSString*)binding toObject:(id)observableController withKeyPath:(NSString*)keyPath options:(NSDictionary*)options<br />{<br />	[super bind:binding toObject:observableController withKeyPath:keyPath options:options];<br />	id obsInfo=[appController observationInfo];<br />	id observance =[[obsInfo valueForKey:@&quot;_observances&quot;] objectAtIndex:2];<br />	fprintf(stderr,&quot;Observance : %s&#092;n&quot;,[[observance&nbsp; description] UTF8String]);<br />	fprintf(stderr,&quot;Property : %s&#092;n&quot;,[[[observance valueForKey:@&quot;_property&quot;] description] UTF8String]);<br />	fprintf(stderr,&quot;Observer : %s&#092;n&quot;,[[[observance valueForKey:@&quot;_observer&quot;] description] UTF8String]);	<br />}
    


    Cela donne :

    Observance : <NSKeyValueObservance 0x14cd30: Observer: 0x14c960, Key path: textViewValue, Options: <New: NO, Old: NO, Prior: NO> Context: 0x0, Property: 0x14ae10>
    Property : <NSKeyValueUnnestedProperty: Container class: AppController, Key: textViewValue, isa for autonotifying: NSKVONotifying_AppController, Key paths of directly and indirectly affecting properties: none>
    Observer : <NSTextValueBinder: 0x14c960>{object: <MyTextView: 0x126bc0>
      Frame = {{0.00, 0.00}, {89.00, 63.00}}, Bounds = {{0.00, 0.00}, {89.00, 63.00}}
        Horizontally resizable: NO, Vertically resizable: YES
        MinSize = {89.00, 63.00}, MaxSize = {463.00, 10000000.00}

    , bindings: value=textViewValue}
Connectez-vous ou Inscrivez-vous pour répondre.