objective-c cocoa cocoa-bindings

objective c - ¿Es necesario anular bind: toObject: withKeyPath: options: en una subclase NSView para implementar el enlace?



objective-c cocoa (3)

Tengo una subclase NSView que tiene propiedades que quiero que sean enlazables. Implementé lo siguiente en la subclase:

myView.h:

@property (readwrite, retain) NSArray *representedObjects;

myView.m:

@synthesize representedObjects; +(void)initialize { [self exposeBinding: @"representedObjects"]; } -(void)bind:(NSString *)binding toObject:(id)observableController withKeyPath:(NSString *)keyPath options:(NSDictionary *)options { if ([binding isEqualToString:@"representedObjects"]) { [observableController addObserver: self forKeyPath:@"arrangedObjects" options:NSKeyValueChangeNewKey context:nil]; } else { [super bind: binding toObject:observableController withKeyPath:keyPath options: options]; } } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if ([keyPath isEqualToString:@"arrangedObjects"]) { [self setRepresentedObjects: [object arrangedObjects]]; } }

Luego creo el enlace a un arrayController en -[AppController awakeFromNib] :

[myView bind:@"representedObjects" toObject:arrayController withKeyPath:@"arrangedObjects" options: nil];

¿Es esta la forma correcta de implementar el enlace? Se trata de un código de placa de caldera que me hace pensar que estoy haciendo algo mal.

Pensé que NSObject implementaría automágicamente lo que he hecho manualmente en -bind:toObject:withKeyPath:options: pero este no parece ser el caso. Si comento mi -bind:toObject:withKeyPath:options: nunca se llama al método setRepresentedObjects.

Información adicional: He investigado un poco más y he llegado a la conclusión de que mi enfoque original es correcto y que debes sobrepasar- -bind:toObject:withKeyPath:options: Aquí hay una cita de Cocoa Bindings Temas de programación: ¿Cómo funcionan las vinculaciones? :

En su enlace: toObject: withKeyPath: options: método, un objeto debe como mínimo hacer lo siguiente:

  • Determine qué enlace se está configurando
  • Registre a qué objeto se lo vincula usando qué keypath y con qué opciones
  • Regístrese como observador del keypath del objeto al que está vinculado para que reciba notificación de cambios

El ejemplo de código en el Listado 2 muestra una implementación parcial del enlace de Joystick: toObject: withKeyPath: options: método que trata solo del enlace de ángulo.

Listado 2 Implementación parcial del método bind: toObject: withKeyPath: options para la clase Joystick:

static void *AngleBindingContext = (void *)@"JoystickAngle"; - (void)bind:(NSString *)binding toObject:(id)observableObject withKeyPath:(NSString *)keyPath options:(NSDictionary *)options { // Observe the observableObject for changes -- note, pass binding identifier // as the context, so you get that back in observeValueForKeyPath:... // This way you can easily determine what needs to be updated. if ([binding isEqualToString:@"angle"]) { [observableObject addObserver:self forKeyPath:keyPath options:0 context:AngleBindingContext]; // Register what object and what keypath are // associated with this binding observedObjectForAngle = [observableObject retain]; observedKeyPathForAngle = [keyPath copy]; // Record the value transformer, if there is one angleValueTransformer = nil; NSString *vtName = [options objectForKey:@"NSValueTransformerName"]; if (vtName != nil) { angleValueTransformer = [NSValueTransformer valueTransformerForName:vtName]; } } // Implementation continues...

Esto muestra claramente que la clase Joystick (que es una subclase NSView) debe sobrescribir -bind:toObject:withKeyPath:options:

Encuentro esto sorprendente. Estaba escéptico de esta conclusión ya que no he encontrado otras muestras de código que hagan esto. Sin embargo, como la documentación oficial de Apple dice que debería andar en -bind:toObject:withKeyPath:options: concluyo que es el enfoque correcto.

¡Estaría muy feliz si alguien pudiera probar que estoy equivocado!


No, no deberías necesitar ese código de pegamento.

¿A qué te refieres con "no parece ser el caso"? ¿Qué pasa si lo omites?


Definitivamente es necesario implementar -bind:toObject:withKeyPath:options: en una vista personalizada si desea implementar enlaces en esa vista. Su implementación en myView.m es bastante acertada.


No, no es necesario anular bind:

Como Peter Hosey escribió en el comentario de la respuesta anterior, puede llamar a exposeBinding: e implementar accesos y arreglos compatibles con KVC y KVO.

MyView.h:

@interface MyView : NSView { NSArray *_representedObjects; } // IBOutlet is not required for bindings, but by adding it you can ALSO use // an outlet @property (readonly, retain) IBOutlet NSArray *representedObjects; @end

MyView.m:

+ (void)initialize { [self exposeBinding:@"representedObjects"]; } // Use a custom setter, because presumably, the view needs to re-draw - (void)setRepresentedObjects:(NSArray *)representedObjects { [self willChangeValueForKey:@"representedObjects"]; // Based on automatic garbage collection _representedObjects = representedObjects; [self didChangeValueForKey:@"representedObjects"]; [self setNeedsDisplayInRect:[self visibleRect]]; }

Luego puede establecer el enlace mediante programación:

[myView bind:@"representedObjects" toObject:arrayController withKeyPath:@"arrangedObjects" options: nil];

Sin embargo, para establecer el enlace en Interface Builder, debe crear una paleta personalizada.