2011-01-13 9 views
34

Aprendí algo nuevo al intentar averiguar por qué mi propiedad readwrite declarada en una categoría privada no generaba un setter. Fue porque mi categoría fue nombrado:Minutia en categorías y extensiones de Objective-C

// .m 
@interface MyClass (private) 
@property (readwrite, copy) NSArray* myProperty; 
@end 

Si lo cambia a:

// .m 
@interface MyClass() 
@property (readwrite, copy) NSArray* myProperty; 
@end 

y mi organismo se sintetiza. Ahora sé que Class Extension no es solo otro nombre para una categoría anónima. Dejar una Categoría sin nombre hace que se metamorfosee en una bestia diferente: una que ahora le da cumplimiento a la implementación del método en tiempo de compilación y le permite agregar ivars. Ahora entiendo las filosofías generales que subyacen a cada una de estas: las categorías se usan generalmente para agregar métodos a cualquier clase en tiempo de ejecución, y las Extensiones de clase generalmente se usan para imponer la implementación de API privada y agregar ivars. Acepto esto

Pero hay pequeñeces que me confunden. Primero, en un nivel alto: ¿Por qué diferenciarse así? Estos conceptos parecen ideas similares que no pueden decidir si son lo mismo o conceptos diferentes. Si son iguales, esperaría que las mismas cosas sean posibles utilizando una Categoría sin nombre como con una Categoría con nombre (que no lo son). Si son diferentes (lo que son), esperaría una mayor disparidad sintáctica entre los dos. Parece extraño decir: "Ah, dicho sea de paso, para implementar una Extensión de clase, simplemente escriba una Categoría, pero omita el nombre. Cambia mágicamente".

En segundo lugar, sobre el tema de la aplicación del tiempo de compilación: si no puede agregar propiedades en una Categoría con nombre, ¿por qué hacerlo convence al compilador de que usted acaba de hacer eso? Para aclarar, lo ilustraré con mi ejemplo. Puedo declarar una propiedad de sólo lectura en el archivo de cabecera:

// .h 
@interface MyClass : NSObject 
@property (readonly, copy) NSString* myString; 
@end 

Ahora, yo quiero a la cabeza hacia el archivo de implementación y yo dar acceso de lectura-escritura privada a la propiedad. Si lo hago correctamente:

// .m 
@interface MyClass() 
@property (readwrite, copy) NSString* myString; 
@end 

recibo una advertencia cuando no sintetizan, y cuando lo haga, puede establecer la propiedad y todo es color de rosa. Pero, frustrante, si da la casualidad que sea ligeramente equivocada acerca de la diferencia entre la categoría y extensión de clase y trato:

// .m 
@interface MyClass (private) 
@property (readwrite, copy) NSString* myString; 
@end 

El compilador está completamente pacificado en el pensamiento de que la propiedad es de lectura-escritura. No recibo ninguna advertencia, y ni siquiera el agradable error de compilación "No se puede establecer el objeto, ya sea propiedad de solo lectura o no encontrado" al configurar myString como lo hubiera hecho si no hubiese declarado la propiedad readwrite en la Categoría. Acabo de recibir la excepción "No responde al selector" en el tiempo de ejecución. Si la adición de ivars y propiedades no es soportada por categorías (nombradas), ¿es demasiado pedir que el compilador juegue con las mismas reglas? ¿Me estoy perdiendo una gran filosofía de diseño?

+0

Creo que quería decir 'readwrite' para las declaraciones de propiedades privadas de myString. Lo edité para reflejar eso; con suerte eso es correcto. –

+0

De hecho lo hice. Gracias. –

+0

Pregunta de Fantasic! –

Respuesta

53

extensiones de clase en Objective-C 2.0 para resolver dos problemas específicos:

  1. permitir que un objeto tiene una interfaz "privado" que se comprueba por el compilador.
  2. Permitir propiedades de escritura pública y de escritura privada.

interfaz privada

Antes de Objective-C 2.0, si un desarrollador quería tener un conjunto de métodos en Objective-C, que a menudo declaró una categoría "privado" en el archivo de implementación de la clase:

@interface MyClass (Private) 
- (id)awesomePrivateMethod; 
@end 

sin embargo, estos métodos privados se mezclan a menudo en el bloque @implementation de la clase (no un @implementation bloque separado del rubro Private). ¿Y por qué no? Estas no son realmente extensiones de la clase; solo compensan la falta de restricciones públicas/privadas en las categorías de Objective-C.

El problema es que los compiladores de Objective-C suponen que los métodos declarados en una categoría se implementarán en otro lugar, por lo que no verifican para asegurarse de que los métodos se implementan. Por lo tanto, un desarrollador podría declararawesomePrivateMethod pero no implementarlo, y el compilador no les advertiría del problema. Ese es el problema que notó: en una categoría, puede declarar una propiedad (o un método) pero no obtener una advertencia si en realidad nunca la implementa, es porque el compilador espera que se implemente "en algún lugar" (lo más probable es que , en otra unidad de compilación independiente de este).

Ingrese las extensiones de clase. Se supone que los métodos declarados en una extensión de clase se implementan en el bloque principal @implementation; si no lo son, el compilador emitirá una advertencia.

públicamente legible privada grabables Propiedades

A menudo es beneficioso para implementar una estructura de datos inmutables - es decir, aquella en la que el código fuera no puede utilizar un regulador para modificar el estado del objeto. Sin embargo, aún puede ser bueno tener una propiedad de escritura para uso interno. Las extensiones de clase permiten que: en la interfaz pública, un desarrollador pueda declarar que una propiedad sea de solo lectura, pero luego declare que se puede escribir en la extensión de clase. Para el código externo, la propiedad será de solo lectura, pero un setter se puede usar internamente.

Entonces, ¿por qué no puedo declarar una propiedad escriturable en una categoría?

Las categorías no pueden agregar variables de instancia.Un colocador a menudo requiere algún tipo de almacenamiento de respaldo. Se decidió que permitir que una categoría declare una propiedad que probablemente requirió una tienda de respaldo fue A Bad Thing ™. Por lo tanto, una categoría no puede declarar una propiedad de escritura.

parecen similares, pero que son diferentes

La confusión radica en la idea de que una extensión de clase es sólo una "Categoría sin nombre". La sintaxis es similar e implica esta idea; Me imagino que fue elegido porque era familiar para los programadores de Objective-C y, de alguna manera, las extensiones de clase son como categorías. Ellos son por igual en que ambas características le permiten agregar métodos (y propiedades) a una clase existente, pero sirven para diferentes propósitos y permiten diferentes comportamientos.

+0

Excelente explicación. Estaba familiarizado con el método de categoría privado pre 2.0 para declarar métodos privados que se implementaron en @implementation de la clase. Aparentemente también me perdí el memorando sobre el advenimiento de la extensión. Para mí, parecía que muchos desarrolladores simplemente optaban por el método menos explícito de no nombrar sus categorías privadas, que no me gustó. La moraleja que necesitaba encontrar era básicamente que no existe una Categoría sin nombre. Gracias. –

+2

Gran respuesta .... – bbum

+0

Gran respuesta, la leí una y otra vez – guoleii

3

Puede agregar una propiedad en una categoría, simplemente no puede sintetizarla. Si usa una categoría, no obtendrá una advertencia de compilación porque espera que el colocador se implemente en la categoría.

+0

Por supuesto. Debería haber pensado en eso. Paso tanto tiempo declarando métodos privados en Categorías que se implementan en la clase @implementation. Pasé por alto que las Categorías generalmente tienen su propia implementación separada. –

8

Está confundido por la similitud sintáctica. Una extensión de clase es no solo una categoría sin nombre. Una extensión de clase es una forma de hacer que una parte de su interfaz sea privada y una parte pública; ambas se tratan como parte de la declaración de interfaz de la clase. Al ser parte de la interfaz de la clase, una extensión se debe definir como parte de la clase.

Una categoría, por otro lado, es una forma de agregar métodos a una clase existente en tiempo de ejecución.Esto podría ser, por ejemplo, en un paquete separado que solo se carga los jueves.

Para la mayor parte del historial de Objective-C, fue imposible agregar variables de instancia a una clase en tiempo de ejecución, cuando se cargan las categorías. Esto se ha trabajado recientemente en el nuevo tiempo de ejecución, pero el lenguaje aún muestra las cicatrices de sus frágiles clases base. Una de ellas es que el lenguaje no admite categorías que agreguen variables de instancia. Tendrá que escribir usted mismo los getters y setters, al estilo de la vieja escuela.

Las variables de instancia en las categorías también son un tanto complicadas. Como no están necesariamente presentes cuando se crea la instancia y el inicializador puede no saber nada al respecto, inicializarlos es un problema que no existe con las variables de instancia normales. se añadieron

+4

+1 por "... solo cargados los jueves". – vikingosegundo

+0

Sí, estoy confundido por la similitud sintáctica =). Puedo ver que las extensiones son un concepto fundamentalmente diferente que las categorías. Me preguntaba por qué una diferencia tan pequeña en sytax conduce a comportamientos tan diferentes. Para aquellos que no están familiarizados con las extensiones (yo, alrededor de ayer), una extensión IS sintácticamente es una categoría sin nombre. –

+0

@Matt Wilding: Tendremos que esperar que uno de los equipos Cocoa de Apple se detenga para obtener una respuesta oficial al "por qué". Las categorías secretas eran la forma antigua de agregar comportamiento "privado" a una clase, por lo que mi suposición es que parecía conveniente usar una sintaxis similar para el reemplazo superior. Dudo que se dieran cuenta de lo confuso que parecería a las personas que no habían estado usando Cocoa todo el tiempo. – Chuck

0

Solo una pequeña aclaración sobre el RAZÓN para el comportamiento diferente de categorías sin nombre (ahora conocidas como Extensiones de clase) y categorías normales (con nombre).

La cosa es muy simple. Puede tener MUCHAS categorías ampliando la misma clase, cargada en tiempo de ejecución, sin que el compilador y el vinculador lo sepan. (considere las muchas hermosas extensiones que las personas escribieron a NSObject, que lo agregan funcionalidad post-hoc).

Ahora Objective-C no tiene ningún concepto de NAME SPACE. Por lo tanto, tener iVars definido en una categoría con nombre podría crear un choque de símbolo en el tiempo de ejecución. Si dos categorías diferentes serían capaces de definir el mismo

@interface myObject (extensionA) { 
NSString *myPrivateName; 
} 
@end 
@interface myObject (extensionB) { 
NSString *myPrivateName; 
} 
@end 

entonces por lo menos, habrá invadido la memoria en tiempo de ejecución.

En contradicción, las extensiones de clase NO TIENEN NOMBRE, por lo que solo puede haber UNA. Es por eso que puedes definir iVars allí. Ellos aseguran que son únicos.

En cuanto a los errores y advertencias del compilador relacionados con categorías y extensiones de clase + ivars y definiciones de propiedad, tengo que aceptar que no son tan útiles, y pasé demasiado tiempo tratando de entender por qué las cosas se compilan o no, y cómo funcionan (si funcionan) después de compilar.

Cuestiones relacionadas