Aspect séquentiel des bindings
Philippe49
Membre
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 :
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 :
@end
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 uppercaseString] copy];<br /> BARATIN; // macro pour le message ajouté dans le label sur l'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:@"value"]){<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:@"textViewValue"])<br /> [appController addToMessage:@"\ntext view observer : text did change"];<br /> // message ajouté dans le label sur l'interface<br />}<br />
@end
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
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 :
On remarque que le textView est passé entièrement en majuscule.
ajouter dans le setter [textView setString:textViewValue] et idem pour le textField.
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
En recodant la méthode de binding, on observe les objets NSKeyValueObservance
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}