objective c - objective - ¿Puedes implementar manualmente las uniones Cocoa?
objective c vs swift (4)
Tuve 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).
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 se libere el controlador de ventana, no antes de que se desasigne , 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 de NSObject
de bind:toObject:withKeyPath:options:
mitad funciona. Propo- ne cambios de bindee a aglutinante (es decir, del modelo / controlador a visualizar), pero no funciona de la manera opuesta. Además, aunque evidentemente se está observando el observeValueForKeyPath:ofObject:change:context:
, observeValueForKeyPath:ofObject:change:context:
nunca se desencadena.
Proyecto de ejemplo aquí: http://www.tomdalling.com/wp-content/BindingsTest.zip
La documentación de Apple indica que, de hecho, tiene que anular bind:toObject:withKeyPath:options:
para implementar enlaces manuales. Vea aquí: http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaBindings/Concepts/HowDoBindingsWork.html
NOTA LATERAL: Investigué cómo funciona el NSAutounbinder indocumentado, y esto es lo que sé.
Cuando se crea un enlace a NSWindowController, el objeto enlazado es en realidad un NSAutounbinder que se adquiere desde el 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 - [NSWindowController release] se llama y retainCount == 1, NSAutounbinder desvincula todos los enlaces a sí mismo. Esto garantiza que no haya punteros colgantes para el objeto antes de desasignarse.
Aquí está la mejor solución que puedo encontrar. Tengo una discusión más detallada y un código de demostración aquí: http://tomdalling.com/blog/cocoa/implementing-your-own-cocoa-bindings/
Básicamente, NO anula bind:toObject:withKeyPath:options:
o unbind:
La implementación predeterminada en NSObject
usará NSAutounbinder
para evitar retener ciclos. Como señaló Louis Gerbarg, todavía hay situaciones en las que NSAutounbinder
no funciona. Sin embargo, puedes hacer que tus enlaces funcionen al menos tan bien como las vinculaciones de Apple.
Debido a que la implementación predeterminada de bind:toObject:withKeyPath:options:
no actualiza el modelo cuando la vista cambia, los cambios orientados a la vista deben propagarse manualmente. Puede usar -[NSObject infoForBinding:]
para obtener toda la información necesaria para actualizar el objeto encuadernado. Agregué mi propio método en NSObject con una categoría:
-(void)propagateValue:(id)value forBinding:(NSString*)binding;
Se encarga de obtener el objeto vinculado, la ruta de la clave encuadernada y aplicar el transformador de valor. La implementación está disponible desde el enlace en la parte superior.
Es posible que desee verificar el protocolo NSKeyValueBindingCreation . Te permite crear enlaces mediante programación mediante código. (Recuerde hacer el trabajo en un método awakeFromNib si necesita hacer referencia a variables de IBOutlet o podrían ser nulas).
La respuesta corta es que no, no puede hacer que funcione sin una solución alternativa en el código de llamada y los plumines. 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.
Una vez dicho esto, hay dos soluciones que son tal vez un poco más bonitas que la anulación en windowWillClose :.
- No se vincule al propietario del archivo, sino que arrastre un NSObjectController como objeto de nivel raíz al plumín y ligue a eso, luego configureContenidos: en el controlador de objetos durante awakeFromNib.
- Activa la 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.
Vea el ejemplo de GraphicsBindings de mmalc para ver un buen ejemplo de cómo implementar sus propios enlaces. Debe implementar el protocolo informal NSKeyValueBindingCreation para que funcione. Para que sus controladores sepan que hay cosas que pueden enlazarse, llame a exposeBinding en el método de inicialización + (id) de su vista:
+ (void)initialize { [self exposeBinding:@"ILIKEBINDAGE"]; }
A continuación, deberá implementar cada uno de los métodos de gestión de enlaces en el protocolo NSKeyValueBindingCreation. 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 el uso del código de pegamento tradicional funcione mejor y sea más fácil de leer.