2009-07-23 25 views
36

He tenido un gran problema al implementar enlaces para mi propia subclase NSView. Funciona, pero hay problemas con los ciclos de retención cuando se vincula al propietario del archivo desde un archivo nib. Después de leerlo un poco, descubrí que Apple tuvo el mismo problema hace unos años, pero lo arregló con alguna clase mágica no documentada (NSAutounbinder).¿Se pueden implementar manualmente las uniones Cocoa?

Hay una larga discusión del problema del ciclo de retención aquí http://www.cocoabuilder.com/archive/message/cocoa/2004/6/12/109600. La solución consiste en desvincular todas las vinculaciones antes de que el controlador de ventana sea lanzado, no antes de que sea desasignado, en un lugar como windowWillClose :. Esto me parece un truco innecesario.

Mi pregunta es esta: ¿hay alguna forma de hacer enlaces personalizados que funcionen tan bien como los creados por Apple, sin usar características no documentadas? ¿Voy por esto de la manera incorrecta?


ACTUALIZACIÓN 2: He encontrado una solución que permite que los enlaces implementados manualmente funcionen exactamente como los enlaces de Apple. Aprovecha la clase NSAutounbinder indocumentada, sin utilizar realmente las características no documentadas. Voy a publicar la solución más tarde hoy.


ACTUALIZACIÓN: He intentado usar exposeBinding:, y no parece hacer ninguna diferencia. Sin embargo, la implementación NSObject de bind:toObject:withKeyPath:options: funciona a medias. Propociona cambios de bindee a aglutinante (es decir, del modelo/controlador a visualizar), pero no funciona de la manera opuesta. Además, aunque obviamente se está observando el bindee, observeValueForKeyPath:ofObject:change:context: nunca se activa.

Proyecto de ejemplo aquí: http://www.tomdalling.com/wp-content/BindingsTest.zip

documentación de Apple indica que usted, de hecho, tiene que anular bind:toObject:withKeyPath:options: para implementar las consolidaciones manuales. Ver aquí: http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaBindings/Concepts/HowDoBindingsWork.html


Nota al margen: He investigado cómo funciona el NSAutounbinder indocumentado, y esto es lo que sé.

Cuando se crea un enlace a un NSWindowController, el objeto enlazado es en realidad un NSAutounbinder que se adquiere del NSWindowController con - [NSWindowController _autounbinder]. NSAutounbinder es un proxy no retenible para el objeto NSWindowController. No se retiene para evitar el problema del ciclo de retención.

Cuando - [Liberación de NSWindowController] se llama y retainCount == 1, El NSAutounbinder desenlaza todos los enlaces a sí mismo. Esto garantiza que no haya punteros colgantes para el objeto antes de desasignarse.

+0

Mientras CocoaBuilder está caído, puede encontrar el hilo relevante en http://lists.apple.com/archives/cocoa-dev/2004//Jun/msg00835.html – s4y

Respuesta

21

Aquí está la mejor solución que puedo encontrar. Tengo un código más detallada discusión y demostración aquí: http://tomdalling.com/blog/cocoa/implementing-your-own-cocoa-bindings/

Básicamente, se NO HACE anulación bind:toObject:withKeyPath:options: o unbind:. La implementación predeterminada en NSObject utilizará NSAutounbinder para evitar retener ciclos. Como señaló Louis Gerbarg, todavía hay situaciones en las que no entra el NSAutounbinder. Sin embargo, puedes hacer que tus enlaces funcionen al menos tan bien como las vinculaciones de Apple.

Como la implementación predeterminada de bind:toObject:withKeyPath:options: no actualiza el modelo cuando la vista cambia, los cambios guiados por la vista deben propagarse manualmente. Puede usar -[NSObject infoForBinding:] para obtener toda la información necesaria para actualizar el objeto encuadernado. He añadido mi propio método de NSObject con una categoría:

-(void)propagateValue:(id)value forBinding:(NSString*)binding; 

Maneja conseguir el objeto dependiente, la ruta de la clave atado, y aplicando el transformador de valor. La implementación está disponible desde el enlace en la parte superior.

+0

Recientemente escribí una respuesta a una pregunta relacionada antes de encontrar esta publicación. ¿Estoy en lo cierto al decir que usar 'exposeBinding:' funciona en casos donde la vista no necesita propagar cambios al modelo? http://stackoverflow.com/questions/366938/is-it-necessary-to-override-bindtoobjectwithkeypathoptions-in-an-nsview-subcl/7394273#7394273 – paulmelnikow

+0

'exposeBinding:' en realidad no hace nada fuera de Interface Builder si recuerdo correctamente. Todo lo que hace es hacer que su enlace aparezca en la GUI del constructor de interfaz. –

2

Es posible que desee comprobar el NSKeyValueBindingCreation Protocol. Te permite crear enlaces mediante programación mediante código. (Recuerde hacer el trabajo en un método awakeFromNib si necesita hacer referencia a las variables de IBOutlet o podrían ser nulas)

+2

Gracias por su respuesta, pero estoy intentando para implementar mis propios enlaces para una clase personalizada, no usar enlaces existentes. –

+0

Creo que para eso es el método + exposeBinding: de ese protocolo. Podría estar equivocado, por supuesto, pero esta parece ser la forma documentada y sancionada de crear tus propias ataduras. –

+0

exposedBindings solo se usa para hacer complementos de compilador de interfaz a partir de lo que he leído –

3

La respuesta corta es que no puede hacer que funcione sin ninguna solución en el código de llamada y plumillas. Incluso NSAutounbinder echa de menos algunos casos para NSDocument y NSWindowController, si Apple no puede hacer que funcione correctamente para 2 clases que preparan especialmente para aquellos de nosotros sin acceso a las entrañas de AppKit no tienen ninguna posibilidad.

Habiendo dicho eso, hay dos soluciones que son quizás un poco más agradables que la desvinculación en windowWillClose :.

  1. no se unen al propietario del archivo, pero en vez de arrastrar un objeto como NSObjectController nivel de la raíz a la punta y se unen a eso, entonces setContents: en el controlador objeto durante awakeFromNib.
  2. Activar recolección de basura. Si esa es una opción, resuelve todos los problemas del ciclo del objeto ;-) Obviamente, GC presenta sus propios problemas, y si necesita compatibilidad con 10.4, no es un iniciador.
2

Vea el ejemplo GraphicsBindings de mmalc para obtener un buen ejemplo de cómo implementar sus propios enlaces. Debe implementar el protocolo informal NSKeyValueBindingCreation para que funcione. Para que sus controladores saben que hay cosas que se pueden unir, llame exposeBinding en el + (id) inicializar método de la vista:

+ (void)initialize { [self exposeBinding:@"ILIKEBINDAGE"]; } 

A continuación, tendrá que aplicar cada una de las fijaciones de la gestión de los métodos en la NSKeyValueBindingCreation protocolo. Básicamente necesita configurar KVO para la vista para que sepa cuándo actualizar en función de los comportamientos de la aplicación y gestionar la limpieza (desvincular :).

Es un código extra bastante feo, por lo que puede ser que usar el código de pegamento tradicional funcione mejor y sea más fácil de leer.

+1

El ejemplo de GraphicsBindings de mmalc tiene el problema de retención de ciclo que mencioné. Además, exposeBinding: ha sido discutido en la respuesta de Ryan Ballantyne. –

+2

desafortunadamente, ese es un enlace muerto. – uchuugaka

Cuestiones relacionadas