Pregunta

Tengo varios años de experiencia en Obj-c y Cocoa, pero ahora estoy volviendo a ello y los avances de Obj-C 2.0, etc.

Estoy tratando de entender el tiempo de ejecución moderno y declarar propiedades, etc. Una cosa que me confunde un poco es la capacidad en el tiempo de ejecución moderno de tener los iVars creados implícitamente. Y, por supuesto, esto implica que en su código siempre debe usar self.property para acceder al valor.

Sin embargo, en los métodos init * y dealloc (suponiendo que no esté usando GC), deberíamos usar el iVar directamente (en el tiempo de ejecución actual).

Entonces las preguntas son:

  1. ¿Deberíamos usar accesores de propiedad en init * y dealloc con Modern Runtime?

  2. Si es así , ¿por qué es diferente? ¿Es solo porque el compilador no puede ver el iVar?

  3. Si necesito anular un descriptor de acceso, ¿puedo acceder a ese iVar que se definirá en tiempo de ejecución o tengo que definir un iVar real que utilizará el tiempo de ejecución?

  4. Nuevamente, si puedo acceder al iVar sintetizado , ¿por qué no puedo continuar haciendo esto para los métodos init * y dealloc?

Leí los documentos varias veces, pero parecían un poco vagos sobre todo esto y quiero estar seguro de que lo entiendo bien para decidir cómo quiero seguir codificando.

Espero que mis preguntas sean claras.


Resumen rápido de pruebas :

  1. Si no declara el ivar en el legado, el compilador es completamente infeliz

  2. Si usa #ifndef __OBJC2__ alrededor de ivar en el compilador heredado es feliz y puede usar tanto ivar directamente como propiedad

  3. En el tiempo de ejecución moderno, puede dejar el ivar sin definir y acceder como propiedad

  4. En tiempo de ejecución moderno, intentar acceder a ivar directamente sin declaración da error durante la compilación

  5. @private declaración de ivar, por supuesto, permite el acceso directo a ivar, tanto en legado como moderno

¿Realmente no da una manera limpia de avanzar ahora?

¿Fue útil?

Solución

En el compilador actual (OS X 10.5 / GCC 4.0.1), no puede acceder directamente a los ivars sintetizados en tiempo de ejecución. Greg Parker, uno de los ingenieros de tiempo de ejecución de OS X lo expresó de esta manera en la lista cocoa-dev (12 de marzo de 2009):

  

No puedes en el compilador actual. UNA   El compilador futuro debería arreglar eso. Utilizar   explícitos ivars privados en el   mientras tanto. Un @private ivar no debería   ser considerado parte del contrato   eso es lo que significa @private, forzado   por advertencias del compilador y enlazador   errores.

     

¿Y por qué no hay una manera de   declarar explícitamente variables de instancia   en el archivo .m para el nuevo tiempo de ejecución?

     

Tres razones: (1) hay algunas   detalles de diseño no triviales para trabajar   fuera, (2) horas de compilación-ingeniero son   limitado, y (3) los ivars privados son   generalmente lo suficientemente bueno.

Entonces, por ahora debe usar la notación de puntos para acceder a las propiedades, incluso en init y dealloc . Esto va en contra de la mejor práctica de usar ivars directamente en estos casos, pero no hay forma de evitarlo. Me parece que la facilidad de usar ivars sintetizados en tiempo de ejecución (y los beneficios de rendimiento) superan esto en la mayoría de los casos. Cuando necesite acceder al ivar directamente, puede usar un ivar privado como sugiere Greg Parker (no hay nada que le impida mezclar ivars explícitamente declarados y sintetizados en tiempo de ejecución).

Actualización Con OS X 10.6, el tiempo de ejecución de 64 bits permite el acceso directo a los ivars sintetizados a través de self- > ivar .

Otros consejos

Dado que las variables de instancia en sí mismas solo pueden sintetizarse en el tiempo de ejecución moderno (y deben declararse en la interfaz @ en 32 bits o antes de Leopard), es más seguro / más portátil también declarar el ivar

  
      
  • ¿Deberíamos usar accesores de propiedad en init * y dealloc con Modern Runtime?
  •   

Mi regla de oro es "posiblemente" para -init * , y " generalmente no " para -dealloc .

Al inicializar un objeto, debe asegurarse de copiar / retener correctamente los valores para ivars. A menos que el establecedor de la propiedad tenga algún efecto secundario que lo haga inapropiado para la inicialización, definitivamente reutilice la abstracción que proporciona la propiedad.

Al desasignar un objeto, desea liberar cualquier objeto ivar, pero no almacenar los nuevos. Una manera fácil de hacer esto es establecer la propiedad en nil ( myObject.myIvar = nil ), que básicamente llama a [myObject setMyIvar: nil] . Dado que los mensajes a cero se ignoran, no hay peligro en esto. Sin embargo, es excesivo cuando [lanzamiento de myIvar]; generalmente es todo lo que necesitas. En general, no use la propiedad (o directamente, el establecedor) en situaciones en las que la desasignación debería comportarse de manera diferente que establecer la variable.

Puedo entender el argumento de eJames contra el uso de accesores de propiedad en init / dealloc, pero la otra cara es que si cambia el comportamiento de la propiedad (por ejemplo, cambia de retener a copiar, o simplemente asignar sin retener) y don ' No lo use en init, o viceversa, el comportamiento también puede desincronizarse. Si la inicialización y modificación de un ivar debe actuar de la misma manera, use el descriptor de acceso de propiedad para ambos.

  
      
  • Si es así, ¿por qué es esto diferente? ¿Es solo porque el compilador no puede ver el ivar?
  •   

El tiempo de ejecución moderno trata el tamaño de clase y el diseño de manera más inteligente, por lo que puede cambiar el diseño de ivars sin tener que volver a compilar subclases. También puede inferir el nombre y tipo del ivar que desea a partir del nombre y tipo de la propiedad correspondiente. La Guía de programación en tiempo de ejecución Objective-C 2.0 tiene más información, pero de nuevo, No sé cuán profundamente se explican los detalles allí.

  
      
  • Si necesito anular un descriptor de acceso, ¿puedo acceder a ese iVar que se definirá en tiempo de ejecución o tengo que definir un iVar real que utilizará el tiempo de ejecución?
  •   

No he probado esto, pero creo que puedes acceder al ivar nombrado en el código, ya que en realidad tiene que ser creado. No estoy seguro de si el compilador se quejará, pero supongo que dado que le permitirá sintetizar el ivar sin quejarse, también es lo suficientemente inteligente como para saber sobre el ivar sintetizado y permitirle referirse a él por su nombre.

  
      
  • Nuevamente, si puedo acceder al iVar sintetizado, ¿por qué no puedo continuar haciendo esto para los métodos init * y dealloc?
  •   

Debería poder acceder a la propiedad y / o ivar en cualquier momento después de que se haya asignado la instancia.

Hay otra pregunta SO con información similar, pero no es un duplicado.

La conclusión, de la documentación de Objective-C 2.0 , y citado de La respuesta de Mark Bessey es la siguiente:

  

Existen diferencias en el comportamiento que dependen del tiempo de ejecución (ver también & # 8220; Diferencias de tiempo de ejecución & # 8221;):

     

Para los tiempos de ejecución heredados, las variables de instancia ya deben declararse en el bloque @interface. Si existe una variable de instancia con el mismo nombre y tipo compatible que la propiedad, se usa & # 8212; de lo contrario, obtendrá un error del compilador.

     

Para los tiempos de ejecución modernos, las variables de instancia se sintetizan según sea necesario. Si ya existe una variable de instancia con el mismo nombre, se utiliza.

Entiendo lo siguiente:

No debe usar accesores de propiedad en los métodos init * y dealloc , por las mismas razones por las que no debe usarlos en el tiempo de ejecución heredado: le deja abierto a posibles errores si luego anula los métodos de propiedad y termina haciendo algo que no debe hacerse en init * o dealloc .

Debería poder sintetizar el ivar y anular los métodos de propiedad de la siguiente manera:

@interface SomeClass
{
}
@property (assign) int someProperty;
@end

@implementation SomeClass
@synthesize someProperty; // this will synthesize the ivar
- (int)someProperty { NSLog(@"getter"); return someProperty; }
- (void)setSomeProperty:(int)newValue
{
    NSLog(@"setter");
    someProperty = newValue;
}
@end

Lo que me lleva a pensar que usted también podría acceder al ivar sintetizado en sus métodos init * y dealloc . Lo único que se me ocurre es que la línea @synthesize puede tener que venir antes de las definiciones de su init * y dealloc métodos en el archivo fuente.

Al final, dado que tener los ivars declarados en la interfaz todavía funciona, esa es su apuesta más segura.

Me encuentro con el mismo problema. La forma en que estoy trabajando para no poder acceder a las variables de instancia sintetizadas es la siguiente:

encabezado público

@interface MyObject:NSObject {
}
@property (retain) id instanceVar;
@property (retain) id customizedVar;
@end

encabezado privado / implementación

@interface MyObject()
@property (retain) id storedCustomizedVar;
@end

@implementation MyObject
@synthesize instanceVar, storedCustomizedVar;
@dynamic customizedVar;

- customizedVar {
  if(!self.storedCustomizedVar) {
    id newCustomizedVar;
    //... do something
    self.storedCustomizedVar= newCustomizedVar;
  }
  return self.storedCustomizedVar;
}

- (void) setCustomizedVar:aVar {
  self.storedCustomizedVar=aVar;
}

@end

No es tan elegante, pero al menos mantiene limpio mi archivo de encabezado público.

Si usa KVO, debe definir customVar como clave dependiente de almacenada CustomizedVar.

Soy relativamente nuevo en Obj-C (pero no en programación) y este tema también me ha confundido.

El aspecto que me preocupa es que parece ser relativamente fácil usar inadvertidamente el iVar en lugar de la propiedad. Por ejemplo escribiendo:

myProp = someObject;

en lugar de

self.myProp = someObject;

Es cierto que este es "usuario" error, pero todavía parece bastante fácil de hacer accidentalmente en algún código, y para una propiedad retenida o atómica, presumiblemente podría ocasionar problemas.

Idealmente, preferiría poder obtener el tiempo de ejecución para aplicar algún patrón al nombre de la propiedad al generar any iVar. P.ej. siempre prefijelas con " _ " ;.

En la práctica, en este momento estoy haciendo esto manualmente, declarando explícitamente mis ivars y dándoles deliberadamente diferentes nombres de las propiedades. Utilizo un prefijo 'm' de estilo antiguo, así que si mi propiedad es '' myProp '', mi iVar será '' mMyProp ''. Luego uso @synthesize myProp = mMyProp para asociar los dos.

Esto es un poco torpe, lo admito, y un poco de tipeo extra, pero me parece que valgo la pena desambiguar un poco más claramente en el código. Por supuesto, todavía puedo equivocarme y escribir mMyProp = someObject, pero espero que el prefijo 'm' me avise de mi error.

Sería mucho mejor si pudiera declarar la propiedad y dejar que el compilador / tiempo de ejecución haga el resto, pero cuando tengo mucho código, mi instinto me dice que cometeré errores de esa manera si todavía tengo que hacerlo. siga las reglas manuales para init / dealloc.

Por supuesto, también hay muchas otras cosas que también puedo hacer mal ...

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top