La capacidad de poner variables de instancia en el @implementation
bloque, o en una extensión de clase, es una característica del “tiempo de ejecución moderno de Objective-C”, que es utilizado por todas las versiones de iOS y por programas Mac OS X de 64 bits.
Si desea escribir aplicaciones Mac OS X de 32 bits, debe poner las variables de instancia en el @interface
declaración. Sin embargo, es probable que no necesite admitir una versión de 32 bits de su aplicación. OS X ha admitido aplicaciones de 64 bits desde la versión 10.5 (Leopard), que se lanzó hace más de cinco años.
Entonces, supongamos que solo está escribiendo aplicaciones que usarán el tiempo de ejecución moderno. ¿Dónde deberías poner tus ivars?
Opción 0: En el @interface
(No lo hagas)
Primero, repasemos por qué no queremos poner variables de instancia en una @interface
declaración.
Poner variables de instancia en un @interface
expone los detalles de la implementación a los usuarios de la clase. Esto puede llevar a esos usuarios (¡incluso a usted mismo cuando usa sus propias clases!) A confiar en detalles de implementación que no deberían. (Esto es independiente de si declaramos los ivars@private
).
Poner variables de instancia en una @interface
hace que la compilación tome más tiempo, porque cada vez que agregamos, cambiamos o eliminamos una declaración ivar, tenemos que recompilar cada .m
archivo que importa la interfaz.
Por lo tanto, no queremos poner variables de instancia en @interface
. ¿Dónde debemos ponerlos?
Opción 2: @implementation
Sin tirantes (No lo hagas)
A continuación, analicemos su opción 2, "Ponga iVars en @implementantion sin bloque de llaves". ¡Esto no declara variables de instancia! Estás hablando de esto:
@implementation Person
int age;
NSString *name;
...
Ese código define dos variables globales. No declara ninguna variable de instancia.
Está bien definir variables globales en su .m
archivo, incluso en su @implementation
, si necesita variables globales, por ejemplo, porque desea que todas sus instancias compartan algún estado, como un caché. Pero no puede usar esta opción para declarar ivars, porque no declara ivars. (Además, las variables globales privadas para su implementación generalmente deben declararse static
para evitar contaminar el espacio de nombres global y correr el riesgo de errores de tiempo de enlace).
Eso deja tus opciones 1 y 3.
Opción 1: En el @implementation
con tirantes (Hazlo)
Por lo general, queremos usar la opción 1: colóquelos en su @implementation
bloque principal , entre llaves, así:
@implementation Person {
int age;
NSString *name;
}
Los colocamos aquí porque mantiene su existencia en privado, evitando los problemas que describí anteriormente, y porque generalmente no hay razón para colocarlos en una extensión de clase.
Entonces, ¿cuándo queremos usar su opción 3, colocándola en una extensión de clase?
Opción 3: en una extensión de clase (hágalo solo cuando sea necesario)
Casi nunca hay una razón para ponerlos en una extensión de clase en el mismo archivo que el de la clase @implementation
. Bien podríamos ponerlos @implementation
en ese caso.
Pero de vez en cuando podemos escribir una clase que sea lo suficientemente grande como para querer dividir su código fuente en varios archivos. Podemos hacer eso usando categorías. Por ejemplo, si estuviéramos implementando UICollectionView
(una clase bastante grande), podríamos decidir que queremos poner el código que administra las colas de vistas reutilizables (celdas y vistas complementarias) en un archivo fuente separado. Podríamos hacer eso separando esos mensajes en una categoría:
@interface UICollectionView : UIScrollView
- (id)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout;
@property (nonatomic, retain) UICollectionView *collectionViewLayout;
@end
@interface UICollectionView (ReusableViews)
- (void)registerClass:(Class)cellClass forCellWithReuseIdentifier:(NSString *)identifier;
- (void)registerNib:(UINib *)nib forCellWithReuseIdentifier:(NSString *)identifier;
- (void)registerClass:(Class)viewClass forSupplementaryViewOfKind:(NSString *)elementKind withReuseIdentifier:(NSString *)identifier;
- (void)registerNib:(UINib *)nib forSupplementaryViewOfKind:(NSString *)kind withReuseIdentifier:(NSString *)identifier;
- (id)dequeueReusableCellWithReuseIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath*)indexPath;
- (id)dequeueReusableSupplementaryViewOfKind:(NSString*)elementKind withReuseIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath*)indexPath;
@end
Bien, ahora podemos implementar los UICollectionView
métodos principales en UICollectionView.m
y podemos implementar los métodos que administran vistas reutilizables enUICollectionView+ReusableViews.m
, lo que hace que nuestro código fuente sea un poco más manejable.
Pero nuestro código de administración de vistas reutilizable necesita algunas variables de instancia. Esas variables deben estar expuestas a la clase principal @implementation
en UICollectionView.m
, por lo que el compilador las emitirá en el .o
archivo. Y también necesitamos exponer esas variables de instancia al código en UICollectionView+ReusableViews.m
, para que esos métodos puedan usar los ivars.
Aquí es donde necesitamos una extensión de clase. Podemos poner los ivars de administración de vistas reutilizables en una extensión de clase en un archivo de encabezado privado:
@interface UICollectionView () {
NSMutableDictionary *registeredCellSources;
NSMutableDictionary *spareCellsByIdentifier;
NSMutableDictionary *registeredSupplementaryViewSources;
NSMutableDictionary *spareSupplementaryViewsByIdentifier;
}
- (void)initReusableViewSupport;
@end
No enviaremos este archivo de encabezado a los usuarios de nuestra biblioteca. Simplemente lo importaremos dentro UICollectionView.m
y fuera UICollectionView+ReusableViews.m
, para que todo lo que necesite ver estos ivars pueda verlos. También hemos incluido un método al que queremos init
que llame el método principal para inicializar el código de administración de vistas reutilizable. Llamaremos a ese método desde -[UICollectionView initWithFrame:collectionViewLayout:]
adentro UICollectionView.m
y lo implementaremos en UICollectionView+ReusableViews.m
.