¿Qué características de Objective-C te gustaría haber conocido antes?

Los objetos asociados (también conocidos como referencias asociativas) son una característica extremadamente útil pero algo oscura del tiempo de ejecución Objective-C de Apple (por lo que técnicamente no se trata de una “característica Objective-C”, sino una característica del tiempo de ejecución de Apple). Está disponible en Mac OS X 10.6+ y iOS 3.1+. y usa las funciones objc_getAssociatedObject() y objc_setAssociatedObject para obtenerlos y configurarlos, respectivamente.

Básicamente, le permite “adjuntar” arbitrariamente un objeto a otro objeto, como si fuera una variable de instancia. Al adjuntar, puede especificar si el objeto secundario debe ser retenido, copiado o no por el padre; si se retiene, se liberará cuando el objeto principal se desasigne (como lo que se suele hacer con las variables de instancia). Esto es útil para almacenar “datos de usuario personalizados” en controles como vistas de alertas, hojas de acciones, botones, etc., que invocan un método delegado o un selector de destino en una acción.

Considere sin objetos asociados, ¿cómo almacenaría los “datos del usuario” para que una vez que se llame a su selector de acción, pueda acceder a los “datos del usuario”? Aquí hay algunas opciones:

  • Almacénelo como una variable de instancia dentro de la clase “padre” que sirve como delegado; sin embargo, esto solo funciona para un número fijo de controles. Si está creando dinámicamente un número ilimitado de botones, por ejemplo, esto no funcionará.
  • Si el control en cuestión es una UIView, use la propiedad “etiqueta”. Sin embargo, esto solo puede almacenar un número entero, no datos complejos arbitrariamente.
  • Subclase el control en cuestión y agregue una variable de instancia a la subclase. Esto funciona. Sin embargo, crear una nueva clase es detallado y molesto; y esto solo funcionará si está creando el control en cuestión, y no funcionará si obtiene el objeto creado por otra persona y desea adjuntarle datos.
  • Mantenga un diccionario desde la dirección del control hasta los datos del usuario. El problema con esto es que no sabrá cuándo el control ya no es necesario (desasignado), por lo que tendrá que seguir reteniendo los datos del usuario, causando una pérdida de memoria si los controles se crean continuamente.
  • Haga que el objeto de datos del usuario sea el delegado u objetivo del control y que sea de una clase personalizada con métodos para manejar la acción de delegado u objetivo. Sin embargo, esto no funcionará, porque los controles no retienen a sus delegados u objetivos, por lo que esto se bloqueará (si nadie retiene al delegado y se desasigna y luego se le envía un mensaje) o debe retenerse en algún lugar indefinidamente , causando una pérdida de memoria.

Los objetos asociados resuelven esto de una manera simple. Podemos configurar el objeto asociado para que se retenga, de modo que no tengamos que preocuparnos por retenerlo por separado, y se liberará automáticamente cuando el control ya no exista. Los objetos asociados también se usan para agregar una categoría de “interfaz de bloque” a las clases existentes basadas en delegados, almacenando el bloque como un objeto asociado.

Desafortunadamente, usar objetos asociados es un poco extraño. Se siente hacky tener que importar “objc / runtime.h” y luego usar funciones C con nombres que comienzan con “objc_”, como si de alguna manera estuvieras hackeando las partes internas de Objective-C. No hay una interfaz conveniente como método de NSObject o algo así. Además, las funciones de los objetos asociados requieren que use una “clave”, que no es una cadena, sino un puntero. Como resultado, la forma estándar de usarlo es crear una variable ficticia no utilizada y usar su dirección como clave (para asegurarse de que no entrará en conflicto con nada más). De nuevo, muy raro.

Categorías Me tomó un tiempo apreciarlo, viniendo de lenguajes de programación más orientados estáticamente como Java y C ++. Pero las categorías son realmente una excelente manera de extender las clases y para una mejor separación de las preocupaciones sin necesidad de subclase.

Caso en cuestión: uso el patrón Active Record en mis clases de Core Data. Por lo tanto, estas clases de datos son “inteligentes” y no solo manejan datos, sino que son capaces de obtener un representante de sí mismo de la red, configurar una vista para mostrarse y varias otras tareas fuera de simplemente almacenar valores y obtenerlos del almacén de datos. Separo estos diversos aspectos de los métodos “sin datos” utilizando categorías de las clases principales.

Así, por ejemplo, una clase de Core Data Foo maneja la extracción y manipulación de datos. Mientras que sus categorías manejan las diversas funcionalidades que no son de datos que necesita hacer:

  • Foo + Net maneja las E / S de la red, obteniendo objetos Foo de un servidor o actualizando el servidor de valores cambiados localmente.
  • Foo + AppKit maneja la configuración de componentes de la interfaz de usuario de OS X para mostrar objetos Foo (por ejemplo, instancias NSTableViewCell).
  • Foo + JSON maneja el mapeo entre objetos JSON basados ​​en servidor y cómo se serializa en JSON.

Sin categorías, la clase Foo puede necesitar incluir todas las funcionalidades anteriores que no están relacionadas con los datos. Alternativamente, esas categorías podrían asignarse a sus propias clases Singleton como Foo_Net, Foo_Appkit y Foo_JSON con cada método tomando un objeto Foo como primer parámetro.