El libro "iOS6 by Tutorials" de Ray Wenderlich tiene un capítulo muy agradable sobre cómo escribir código Objective-C más "moderno". En una sección, los libros describen cómo mover iVars del encabezado de la clase al archivo de implementación. Dado que todas las iVars deberían ser privadas, esto parece ser lo correcto.
Pero hasta ahora encontré 3 formas de hacerlo. Todos lo hacen de manera diferente.
1.) Ponga iVars debajo de @implementantion dentro de un bloque de llaves (así es como se hace en el libro).
2.) Ponga iVars en @implementantion sin bloque de llaves
3.) Coloque iVars dentro de la interfaz privada sobre @implementantion (una extensión de clase)
Todas estas soluciones parecen funcionar bien y hasta ahora no he notado ninguna diferencia en el comportamiento de mi aplicación. Supongo que no hay una forma "correcta" de hacerlo, pero necesito escribir algunos tutoriales y solo quiero elegir una forma para mi código.
¿Qué camino debo tomar?
Editar: solo estoy hablando de iVars aquí. No propiedades. Solo variables adicionales que el objeto necesita solo para sí mismo y que no deben exponerse al exterior.
Muestras de código
1)
#import "Person.h"
@implementation Person
{
int age;
NSString *name;
}
- (id)init
{
self = [super init];
if (self)
{
age = 40;
name = @"Holli";
}
return self;
}
@end
2)
#import "Person.h"
@implementation Person
int age;
NSString *name;
- (id)init
{
self = [super init];
if (self)
{
age = 40;
name = @"Holli";
}
return self;
}
@end
3)
#import "Person.h"
@interface Person()
{
int age;
NSString *name;
}
@end
@implementation Person
- (id)init
{
self = [super init];
if (self)
{
age = 40;
name = @"Holli";
}
return self;
}
@end
fuente
Respuestas:
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 declararsestatic
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:// UICollectionView.h @interface UICollectionView : UIScrollView - (id)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout; @property (nonatomic, retain) UICollectionView *collectionViewLayout; // etc. @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 enUICollectionView.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
enUICollectionView.m
, por lo que el compilador las emitirá en el.o
archivo. Y también necesitamos exponer esas variables de instancia al código enUICollectionView+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:
// UICollectionView_ReusableViewsSupport.h @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 fueraUICollectionView+ReusableViews.m
, para que todo lo que necesite ver estos ivars pueda verlos. También hemos incluido un método al que queremosinit
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:]
adentroUICollectionView.m
y lo implementaremos enUICollectionView+ReusableViews.m
.fuente
readonly
propiedad para que sea de forma privadareadwrite
, debe usar una extensión de clase. Pero la pregunta era dónde colocar los ivars, no dónde colocar las declaraciones de propiedad. No veo cómo el uso de una extensión de clase puede evitar que la implementación acceda a ivars. Para hacer eso, necesitaría poner las implementaciones de su método en una categoría, porque el compilador debe ver todas las extensiones de clase que declaran ivar antes de ver la extensión@implementation
.La opción 2 es completamente incorrecta. Esas son variables globales, no variables de instancia.
Las opciones 1 y 3 son esencialmente idénticas. No hace absolutamente ninguna diferencia.
La elección es si poner variables de instancia en el archivo de encabezado o en el archivo de implementación. La ventaja de usar el archivo de encabezado es que tiene un atajo de teclado rápido y fácil (Comando + Control + Arriba en Xcode) para ver y editar sus variables de instancia y declaración de interfaz.
La desventaja es que expones los detalles privados de tu clase en un encabezado público. Eso no es deseable en algunos casos, especialmente si está escribiendo código para que lo usen otros. Otro problema potencial es que si está utilizando Objective-C ++, es bueno evitar poner cualquier tipo de datos C ++ en su archivo de encabezado.
Las variables de instancia de implementación son una excelente opción para ciertas situaciones, pero para la mayor parte de mi código todavía coloco las variables de instancia en el encabezado simplemente porque es más conveniente para mí como codificador que trabaja en Xcode. Mi consejo es que hagas lo que consideres más conveniente para ti.
fuente
En gran parte, tiene que ver con la visibilidad del ivar a las subclases. Las subclases no podrán acceder a las variables de instancia definidas en el
@implementation
bloque.Para el código reutilizable que planeo distribuir (por ejemplo, código de biblioteca o marco) donde prefiero no exponer las variables de instancia para la inspección pública, entonces me inclino a colocar los ivars en el bloque de implementación (su opción 1).
fuente
Debe colocar las variables de instancia en una interfaz privada por encima de la implementación. Opción 3.
La documentación para leer sobre esto es la guía Programación en Objective-C .
De la documentación:
fuente
Los ivars públicos realmente deberían ser propiedades declaradas en @interface (probablemente lo que estás pensando en 1). Los ivars privados, si está ejecutando el último Xcode y utiliza el tiempo de ejecución moderno (OS X o iOS de 64 bits), se pueden declarar en @implementation (2), en lugar de en una extensión de clase, que es probablemente lo que ' estoy pensando en 3.
fuente