usos sistema programacion origen operativo lenguaje caracteristicas apple objective-c

objective c - sistema - Minutia en categorías y extensiones de Objective-C



swift versions (4)

Estás confundido por la similitud sintáctica. Una extensión de clase no es 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.

Durante la mayor parte de la historia 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.

Aprendí algo nuevo mientras trataba de descubrir por qué mi propiedad readwrite declarada en una categoría privada no generaba un setter. Fue porque mi Categoría fue nombrada:

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

Cambiándolo a:

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

y mi setter está sintetizado. 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 imposició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 hizo exactamente eso? Para aclarar, lo ilustraré con mi ejemplo. Puedo declarar una propiedad de solo lectura en el archivo de encabezado:

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

Ahora, quiero dirigirme al archivo de implementación y darme acceso de lectura privada a la propiedad. Si lo hago correctamente:

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

Recibo una advertencia cuando no sintetizo, y cuando lo hago, puedo establecer la propiedad y todo es color de rosa. Pero, frustrantemente, si me equivoco un poco acerca de la diferencia entre Categoría y Extensión de Clase e intento:

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

El compilador se tranquiliza por completo al pensar que la propiedad es readwrite. 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?


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

  1. Permita que un objeto tenga una interfaz "privada" que compruebe el compilador.
  2. Permitir propiedades de lectura 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, a menudo declaraban una categoría "Privada" en el archivo de implementación de la clase:

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

Sin embargo, estos métodos privados a menudo se mezclaron en el bloque @implementation la clase ( no en un bloque @implementation separado para la categoría 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 se verifican para asegurarse de que los métodos se implementen. Por lo tanto, un desarrollador podría declarar awesomePrivateMethod pero no implementarlo, y el compilador no les advertiría sobre el 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 @implementation principal; si no lo son, el compilador emitirá una advertencia.

Propiedades de lectura pública y de escritura privada

A menudo es beneficioso implementar una estructura de datos inmutables, es decir, una en la que el código externo no puede usar un colocador para modificar el estado del objeto. Sin embargo, todavía 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 que se puede escribir 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.

Se ven similares, pero son diferentes

La confusión radica en la idea de que una extensión de clase es solo 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. Son parecidos en que ambas características le permiten agregar métodos (y propiedades) a una clase existente, pero sirven para diferentes propósitos y, por lo tanto, permiten diferentes comportamientos.


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.


Solo una pequeña aclaración sobre la 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 pudieran definir el mismo

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

entonces, como mínimo, habrá memoria rebasada en tiempo de ejecución.

En contradicción, las extensiones de clase no tienen NOMBRE, y por lo tanto solo puede haber UNO. Es por eso que puedes definir iVars allí. Ellos aseguran que son únicos.

En cuanto a los errores del compilador y las advertencias relacionadas con las categorías y extensiones de clase + ivars y las 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.