2010-09-20 16 views
17

Decir que tengo una clase como esta:¿Debo usar propiedades o referencia directa cuando acceda a variables de instancia internamente?

@interface MyAwesomeClass : NSObject 
{ 
@private 
    NSString *thing1; 
    NSString *thing2; 
} 
@property (retain) NSString *thing1; 
@property (retain) NSString *thing2; 
@end 

@implementation MyAwesomeClass 
@synthesize thing1, thing1; 
@end 

Al acceder thing1 y thing2internamente (es decir, dentro de la implementación de MyAwesomeClass), es mejor utilizar la propiedad, o simplemente hacer referencia a la variable de instancia directamente (suponiendo casos en los que no hacemos ningún trabajo en un acceso "personalizado" o mutador, es decir, simplemente establecemos y obtenemos la variable). Pre-Objective C 2.0, usualmente solo accedemos a los ivars directamente, pero ¿cuál es el estilo de codificación/mejores prácticas usuales ahora? ¿Y esta recomendación cambia si una variable/propiedad de instancia es privada y no se puede acceder fuera de la clase? ¿Debes crear una propiedad para cada ivar, incluso si son privados, o solo para datos públicos? ¿Qué pasa si mi aplicación no utiliza las características de codificación de valor clave (ya que KVC solo activa el acceso a la propiedad)?

Me interesa mirar más allá de los detalles técnicos de bajo nivel. Por ejemplo, dada (sub-óptimo) código como:

@interface MyAwesomeClass : NSObject 
{ 
    id myObj; 
} 
@proprety id myObj; 
@end 

@implementation MyAwesomeClass 
@synthesize myObj; 
@end 

Sé que myObj = anotherObject es funcionalmente el mismo que self.myObj = anotherObj.

Pero las propiedades no son simplemente una sintaxis sofisticada para instruir al compilador para que cree accesos y mutadores para usted, por supuesto; también son una forma de encapsular mejor los datos, es decir, puede cambiar la implementación interna de la clase sin reescribir las clases que dependen de esas propiedades. Me interesan las respuestas que abordan la importancia de este problema de encapsulación cuando se trata del código interno de la clase. Además, las propiedades escritas adecuadamente pueden disparar las notificaciones de KVC, pero el acceso directo de ivar no; ¿Importa esto si mi aplicación no está utilizando las características de KVC ahora, por si acaso podría en el futuro?

Respuesta

8

No creo que ninguna forma sea 'mejor'. Verá que ambos estilos son de uso común, por lo que ahora ni siquiera hay una práctica usual/mejor. En mi experiencia, el estilo utilizado tiene muy poco impacto en cómo digerir algunos archivos de implementación que estoy buscando. Sin duda, desea sentirse cómodo con ambos estilos (y cualquier otro) al mirar el código de otras personas.

El uso de una propiedad para cada ivar interno podría ir un poco por la borda, en términos de mantenimiento. Lo hice y agregó una cantidad de trabajo no trivial que no creo que me haya pagado. Pero si tiene un gran deseo/OCD por ver un código consistente como self.var en todas partes, y lo tiene en el fondo de su mente cada vez que mira una clase, entonces úselo. No descarte el efecto que una sensación persistente puede tener en la productividad.

Excepciones: Obviamente, para los captadores personalizados (por ejemplo, creación diferida), no tiene muchas opciones. Además, creo y uso una propiedad para setters internos cuando lo hace más conveniente (por ejemplo, establecer objetos con semántica de propiedad).

"just in case", "might" no es una razón convincente para hacer algo sin más datos, ya que el tiempo requerido para implementarlo no es cero. Una mejor pregunta podría ser, ¿cuál es la probabilidad de que todos los ivars privados de alguna clase requieran notificaciones de KVC en el futuro, pero no ahora? Para la mayoría de mis propias clases, la respuesta es extremadamente baja, así que ahora evito una regla difícil sobre la creación de propiedades para cada ivar privado.

He encontrado que cuando se trata de implementaciones internas, rápidamente consigo una buena idea de cómo se debe acceder independientemente de cada ivar.

Si usted está interesado, mi propio enfoque es la siguiente:

  • Ivars leyendo: Acceso directo, a menos que haya un captador personalizado (por ejemplo, la creación perezoso)
  • Ivars de escritura: Directamente en alloc/dealloc. En otro lugar, a través de una propiedad privada si existe.
3

La única diferencia en una cesión de thing1 = something; y self.thing1 = something; es que si usted quiere tener la operación de asignación de propiedad (retain, copy, etc), realizado sobre el objeto asignado, entonces es necesario utilizar una propiedad. Asignar sin propiedades será exactamente eso, asignando una referencia al objeto provisto.

Creo que no es necesario definir una propiedad para los datos internos. Solo defina propiedades para ivars a los que se accederá con frecuencia y que necesiten un comportamiento de mutador específico.

11

Si pasas tiempo en la lista de correo de cocoa-dev, encontrarás que este es un tema muy polémico.

Algunas personas piensan que los ivars solo deben usarse internamente y que las propiedades nunca se deben usar (o raramente), excepto externamente. Existen varias preocupaciones con las notificaciones de KVO y los efectos secundarios del accesorio.

Algunas personas piensan que usted siempre (o principalmente) debe usar propiedades en lugar de ivars. La principal ventaja aquí es que su gestión de memoria está bien contenida dentro de los métodos de acceso en lugar de dispersarse en su lógica de implementación. Las notificaciones de KVO y los efectos secundarios del acceso pueden superarse mediante la creación de propiedades separadas que apuntan al mismo ivar.

Al mirar el código de muestra de Apple, se verá que están por todas partes en este tema. Algunas muestras usan propiedades internamente, otras usan ivars.

Yo diría, en general, que esto es una cuestión de gusto y que no hay una forma correcta de hacerlo. Yo mismo uso una mezcla de ambos estilos.

+0

Gracias. Tú y Allen Ding tuvieron respuestas especialmente buenas. – mipadi

2

Si thing1 se utiliza con MVA es una buena idea usar self.thing1= cuando la coloque. Si thing1 es @public, entonces es mejor asumir que alguien algún día querrá usarlo alguna vez con KVO.

Si thing1 tiene la semántica de ajuste complejas que no desee repetir todas partes lo establece (por ejemplo retain, o no nonatomic) a continuación, utilizar a través self.thing1= es una buena idea.

Si la evaluación comparativa muestra que llamar a setThing1: lleva mucho tiempo, entonces quizás desee pensar en cómo configurarlo sin usar self.thing1= - tenga en cuenta que no se puede aplicar KVO o ver si implementar KVO manualmente es mejor (por ejemplo, si lo configura 3000 veces en un bucle en algún lugar, es posible que pueda establecerlo a través de self->thing1 3000 veces, y realice 2 llamadas KVO sobre el valor que está a punto de cambiar y haber cambiado).

Eso deja el caso de un setter trivial en una variable privada donde sabe que no está utilizando KVO. En ese punto, deja de ser un problema técnico y cae dentro del estilo del código. Al menos mientras el acceso no aparezca como un cuello de botella en el generador de perfiles.Tiendo a usar el acceso directo de ivar en ese punto (a menos que crea que KVO tendrá ese valor en el futuro, o podría querer hacerlo público y, por lo tanto, creo que otros pueden querer KVO).

Sin embargo, cuando configuro cosas con acceso directo a ivar solo trato de hacerlo a través de self->thing1=, eso hace que sea mucho más simple encontrarlas todas y cambiarlas si alguna vez encuentro la necesidad de usar KVO, o hacerlo público , o para hacer un accesorio más complejo.

0

Otras cosas mencionadas aquí están bien. Algunas cosas que las otras respuestas perdieron son:

Primero, siempre tenga en cuenta las implicaciones de los accessors/mutators siendo virtuales (como todos los métodos de Objective-C). En general, se ha dicho que se debe evitar la llamada virtual métodos en init y dealloc, porque no sabes lo que hará una subclase que podría arruinarte. Por esta razón, generalmente trato de acceder a iVars directamente en init y dealloc, y accedo a ellos a través del accessor/mutators en cualquier otro lugar. Por otro lado, si no usa sistemáticamente los accesadores en todos los demás lugares, las subclases que los reemplacen pueden verse afectados.

Relacionado, las garantías de propiedad de atomicidad (es decir, sus @properties son declaradas atómicas) no se pueden mantener para nadie si está accediendo directamente al iVar en cualquier lugar fuera de init & dealloc. Si necesitabas algo para ser atómico, no deseches la atomicidad accediendo directamente al iVar. Del mismo modo, si no necesita esas garantías, declare su propiedad como no atómica (por rendimiento)

Esto también se relaciona con el problema de KVO. En init, nadie puede estar observándole todavía (legítimamente), y en dealloc, cualquier observador restante tiene una referencia obsoleta no retenida (es decir, falsa). El mismo razonamiento también se aplica a las garantías de atomicidad de las propiedades. (es decir, ¿cómo ocurrirían los accesos concurrentes antes de que inicie init y los accesos que suceden durante dealloc son errores intrínsecos)

Si combina el uso directo y el acceso/mutador, se corre el riesgo de no solo KVO y atomicidad, sino de subclassers también.

Cuestiones relacionadas