Éramos pocas arrobas y parió la abuela: @import y módulos en Objective C

Autor: | Última modificación: 12 de marzo de 2024 | Tiempo de Lectura: 4 minutos
Temas en este post:
Una de las novedades en iOS 7 con la que tal vez ya te hayas encontrado es la directiva @import:
// Donde dije:
#import <UIKit/UIKit.h>
// ahora digo
@import UIKit;
La directiva @import indica una gran novedad en el lenguaje Objective C, y de hecho, en todos los lenguajes de la familia de C: los módulos (o módules en Inglés).

¿Como funciona #import en Objective C?

Cuando el compilador está procesando un fichero .m y se encuentra con un #import <UIKit/UIKit.h>, hace lo siguiente:
  1. Va a la carpeta UIKit (que está en un directorio conocido por el compilador)
  2. Copia el contenido del fichero UIKit.h y lo pega en el .m, donde antes ponía #import <UIKit/UIKit.h>
  3. Como el contenido de UIKit.h es un montón de otros #import que apuntan a otros ficheros de cabecera, el compilador repite el proceso.
  4. Cuando finalmente ya no quedan más ficheros de cabecera que importar (es decir, copiar y pegar) es cuando se compila el fichero .m gigante que ha resultado de tanto copiar y pegar.
Esto es lo que se viene haciendo en C y Objective C desde hace eras geológicas. Como os podéis imaginar, es un proceso lento, primitivo y proclive a errores, puesto que el en fondo no es más que un copiar y pegar con un nombre más bonito. Uno de los errores posibles, es el importado recursivo:
@import
¡Bienvenido al apasionante mundo de la recursividad! 😉
En los casos más sencillos, #import se percatará del ciclo y no se meterá en un bucle infinito, pero el amigo Murphy se encarga de que no siempre sea así. Para evitar este problema usamos @class, tal y como se explica en el Curso de Programación iOS o el Curso Online de Programación iOS.
Éramos pocas arrobas y parió la abuela: @import y módulos en Objective C 1
Contra Murphy, ni @class es defensa.
Hay otro problema con este sistema de copiar y pegar, del cual ni @class nos libra. Si tienes 10 ficheros de implementación en tu proyecto (y eso es algo muy pequeño), y cada uno de ellos importan, de forma directa o indirecta a 50 ficheros de cabecera (si esto te parece una exageración, echa un vistazo a Foundation.h o UIKit.h), el total de ficheros que tendrá que procesar el compilador será de 10×50: 500. Cada vez que compilas, indexas o analizas. Claramente esto no es escalable…. En iOS no es un gran problema, ya que UIKit es relativamente pequeño, pero en el caso del «primo de Zumosol» (OSX), la cosa se pone pero que muy fea.
Éramos pocas arrobas y parió la abuela: @import y módulos en Objective C 2
Cuando digo que la cosa se pone fea, es porque se pone fea de cojones.

Fichero de cabecera precompilado (.pch) al rescate

Una forma de resolver esto, es de compilar todo UIKit, Foundation y demás frameworks del sistema 1 sola vez y luego simplemente enlazarlo.
// Contenido de un pch
#import <Availability.h>

#ifndef __IPHONE_5_0
#warning "This project uses features only available in iOS SDK 5.0 and later."
#endif

#ifdef __OBJC__
    #import <UIKit/UIKit.h>
    #import <Foundation/Foundation.h>
    #import <iAd/iAd.h>
#endif
Con esto, además se logra que esos ficheros de cabecera pasen a estar «mágicamente» disponibles en todos los demás ficheros de tu proyecto. Es decir, si tienes UIKit importado en el pch, no hace falta volverlo a importar en otra parte (aunque si lo haces, no pasa nada). Lo malo del fichero de cabecera precompilado (o pch), es precisamente que hace un solo paquete de un montón de cosas distintas (en el caso de arriba, Foundation, UIKit y iAd). No siempre me interesa tener todo eso, especialmente cosas más específicas como iAd, disponible en todas mis clases.
The Thing 2011 4
¿Juntos y revueltos en el pch? No gracias.
Lo interesante sería que cada framework del sistema fuese una especie de precompilado. Es decir, que no necesite de ese copiar y pegar infernal que genera #import y que retrasa la compilación. Pues bien, eso existe, y son los famosos módulos de Objective C (o modules en Inglés).

Módulos (modules) en Objective C e iOS 7

Un módulo es todo el código de una framework, empaquetado que tal forma que facilita su inclusión en tus clases. Con los módulos tienes todas las ventajas del fichero de cabecera precompilado (pch) y algunos extras:
  1. No está todo junto y revuelto, sino que cada framework es un módulo a parte
  2. No hace falta incluir la framework en tu proyecto, cuando incluyes el módulo en el código, lo demás se hace por ti.
Los módulos se incluyen con la directiva @import:
@import iAd;        // ¡No hace falta añadir la framework en el proyecto!
@import CoreData;   // ¡No hace falta añadir la framework en el proyecto!

Submódulos en Objective C e iOS 7

Además, un módulo puede estar compuesto de varios submódulos. Supongamos que no necesito TODO iAd en mi App, sino tan sólo ADBannerView. No tendría mucho sentido importar TODA la framework. Esto también lo resuelven los módulos: importa sólo el submódulo que necesitas:
@import iAd.ADBannerView; // Solo lo que necesito, gracias.

 ¡Me encanta @import! pero…¿donde está la pega?

YouTube video

Escucha y aprende, pequeño saltamontes

Efectivamente, pequeño saltamontes, siempre hay una pega y es la siguiente: sólo Apple puede crear módulos. Es decir, solo puedes usar @import para código de Apple. Con tu código, NO puedes usar @import. Los pobres seguimos condenados a usar #import. 😛 Hay otra pega, y es la siguiente, NO se puede usar @import para C++ o para ObjectiveC++. Entonces ¿qué hago? ¿Uso @import o #import? Mi recomendación es la siguiente: Proyectos Antiguos Simplemente activa los módulos en los «Build Settings» del proyecto y no cambies nada en tu código. Es decir, deja los #import tal y como están. Si activas los módulos en los settings, Xcode transformará los #import de frameworks del sistema por @import y tendrás las ventajas de los módulos (mayor velocidad de compilación) sin tener que hacer nada. Por la cara, vamos.
Éramos pocas arrobas y parió la abuela: @import y módulos en Objective C 3
¡Aquí, aquí!
Proyectos Nuevos Olvídate del pch y usa @import para todas las frameworks del sistema. Sigue usando #import para tu propio código.

Como crear tus propios módulos

En el futuro es muy probable que puedas crear tus propios módulos. Esa será la mejor forma de empaquetar tus frameworks. Todo lo necesario para hacerlo está ya listo, y solo falta que Apple le dé su bendición. Seguramente la próxima versión de Xcode lo incluya. Mientras tanto, os dejo un par de enlaces para el que quiera saber más. Estos dos enlaces son cortesía de Graham Lee. Graham recientemente se ha unido al Big Nerd Ranch y va a echarme una mano con el curso Advanced iOS Bootcamp en Holanda este mes. Charlando sobre estas cosas, me pasó los ds enlaces en cuestión. Asíq ue ya sabeis, cuando le veáis, a dar las gracias con una cervecita. 😉