2009-08-14 10 views
10

Esta no es una pregunta sobre el estilo. Se trata más sobre el uso adecuado del idioma en sí. Soy bastante nuevo en la programación y soy totalmente nuevo en Objective-C y Cocoa, pero después de leer sobre el lenguaje y consultar algunos ejemplos de código, aparecen algunos patrones de uso que no tienen sentido para mí. O más bien, parecen no ser óptimos en mi mente. Espero que todos puedan ayudar a educarme sobre el uso apropiado de algunos de estos constructos de lenguaje.Forma correcta de usar Objective C

las interfaces vs protocolos

que entienden los conceptos de interfaces y aplicación en lo que respecta a la abstracción. Sin embargo, el patrón dominante que veo en código de ejemplo de Objective-C es la siguiente ...

@interface Foo : NSObject 
{ 
    some ivars decls 
} 
some methods decls 
@end 

Y a continuación, el código de cliente en forma de ...

Foo* f = [[Foo alloc] init]; 

Esto parece extraño a mi. Foo parece ser una implementación en mi mente (independientemente de la desafortunada denominación de la palabra clave @interface). Las interfaces en mi mente no exponen variables de instancia. Un cliente de esta clase no debe estar expuesto a los detalles de mi implementación.

Objective-C tiene la palabra clave @protocol que, en mi opinión, es más una interfaz que la palabra clave @interface. Le permite definir los métodos y/o propiedades y eso es todo. Sin implementación. No hay variables de instancia. Solo interfaz.

@protocol Foo <NSObject> 
    some methods 
    maybe some properties 
@end 

@interface FooProvider : NSObject 
    + (FooProvider*) sharedProvider; 
    - (id<Foo>) fooWithBlahBlah; 
@end 

lo tanto, el código de cliente podría tomar la forma de ...

id<Foo> f = [[FooProvider sharedProvider] fooWithBlahBlah]; 
[f whatever]; 

Esto parece (al menos para mí) para ser un uso más abstracto de la lengua y aislamientos clientes de detalles de implementación de los cuales no deben depender. Mi pregunta es: ¿qué debo esforzarme por seguir? Entiendo que puede haber situaciones en las que una es preferible a la otra, pero en términos generales, ¿debería haber una norma y una sería la excepción?

Alguna orientación sería apreciada.

Gracias, romana

Respuesta

10

puedo ver donde está viniendo de en lo que respecta a la denominación de la palabra clave @interface, pero Objective-C utiliza las dos palabras clave @interface y @implementation para diferenciar entre la declaración de clase (interfaz) y su definición (aplicación). La parte @interface generalmente se coloca en el archivo de encabezado de clase, y la parte @implementation va en el archivo de origen. Estoy de acuerdo con usted al 100% en que las variables de instancia interna realmente no deberían aparecer en el archivo de encabezado. No estoy seguro de lo que llevó a esa decisión, pero mi mejor estimación es que tiene que ver con la herencia de objetos. Cuando se hereda de una clase padre:

#import "ParentClass.h" 
@interface MyClass : ParentClass 
... 

También hereda todas las variables de instancia, lo que sería muy difícil para el compilador de averiguar a menos que esas variables se declaran en la cabecera de la clase.

También tiene razón sobre el uso de @protocol, ya que básicamente define una interfaz que una clase debe cumplir. Los protocolos parecen ser la respuesta de Objective-C a la herencia múltiple.

En mi experiencia con Objective-C, los protocolos no se usan con mucha frecuencia. Tienen su lugar, pero la mayoría del código usa las palabras clave básicas @interface y @implementation para definir una clase, y los protocolos solo se usan cuando se requiere alguna funcionalidad adicional que no pertenece a la jerarquía de clases, pero aún necesita ser compartido en múltiples clases.

Si usted está en busca de orientación, diría:

  1. Aceptar la denominación de las @interface y @implementation palabras clave, incluso si no lo hace bastante jive con lo que espera. Cuando programe en Objective-C, beba el Kool-Aid y vea esas palabras clave de la manera que los diseñadores del lenguaje lo intentaron.
  2. Utilice los protocolos cuando tengan sentido en su código, pero no los use en exceso para intentar que Objective-C se comporte más como otro idioma. Podría funcionar, pero otras personas se avergonzarán cuando vean su código y dificultará la colaboración.
  3. Sigue haciendo tan buenas preguntas. ¡Has mencionado algunos puntos muy válidos!
+0

Buena respuesta, especialmente para abordar la pregunta del OP sobre la instancia privada variables en el archivo de encabezado. Simplemente agregando un enlace a otra pregunta sobre SO sobre ese problema en particular: http://stackoverflow.com/questions/966893/why-are-instance-variables-defined-in-the-header-file-in-objective-c –

+0

Objetivo -C es una capa realmente delgada sobre C. En realidad solo se trata de administrar algunas tablas de símbolos durante la compilación; muy similar al preprocesador c en sí. Estoy seguro de que la mayoría de las decisiones fueron para facilitar la codificación (como utilizar llaves cuadradas en lugar de parens, tenían que encontrar algo que pudieran analizar fácilmente y extraer del código circundante. Probablemente la razón por la que eligieron seguir la sintaxis de Lisp como bien - más fácil de analizar/extraer/reemplazar. Sin embargo, creo que el archivo de encabezado es solo de entrada, no solo un reemplazo simbólico, por eso es por eso que ponen variables ahí. –

+1

No estaban siguiendo la sintaxis de Lisp, sino más bien la sintaxis de Smalltalk. Smalltalk intenta manejar tanto como sea posible (incluidas las declaraciones condicionales) a través del uso uniforme del paso de mensajes, y la sintaxis refleja este principio de diseño básicamente consistente en bloques anidados. – Felixyz

12

En primer lugar, es necesario comprender que se está cometiendo un error clásico: que el lenguaje que ya sabe define los términos utilizados en todos los idiomas - como si un idioma en particular ha establecido una canónica nomenclatura.

Java se inició en 1990 internamente en Sun. Objective-C ya había sido lanzado en 1986. Entonces, en todo caso, la terminología de la interfaz de Java está en desacuerdo con Objective-C, no al contrario. Sin embargo, la interfaz tiene una historia mucho más larga como término que cualquiera de estos lenguajes.

La idea detrás de la @interface en Objective-C se indica en el Objective-C 2.0 Programming Language Reference:

interfaz parte de una especificación de la clase Objective-C que declara su interfaz pública, que incluyen el nombre de la superclase, las instancias variables y prototipos de método público.

métodos de instancia son métodos de clase que son públicos si se declaran en la interfaz. si se declaran en @implementation, son privados.

La implementación entra en @implementation. Quizás lo que te confunde es que Java no tiene la noción de una sección de declaración de clase como en Objective-C. La declaración de clase declara la interfaz de clase de una manera genérica, sin detalles de la implementación. @implementation tiene la implementación real, que es los detalles de cómo se implementa la interfaz @ de clase.

No está mal que Object-c sea diferente de Java, simplemente es diferente.

Pero tiene razón, los protocolos son los análogos más cercanos a las interfaces Java en Object-C que va a encontrar. No es una superclase para nada, y no tiene ninguna implementación asociada. Usted proporciona protocolos de la misma manera que lo haría con las interfaces en Java.

Sin embargo, tenga en cuenta que los protocolos no son tan comunes como las clases en Objective-C. Eso debería ayudar a guiar tu pensamiento.

+0

Lo siento si salí al comparar Objective-C con Java. Esa no era mi intención. La palabra Java nunca apareció en mi publicación original. Simplemente estaba expresando mi conflicto interno sobre lo que considero una interfaz y lo que Objective-C considera como una interfaz. El consejo de simplemente hacer lo que hace el otro código no me sienta bien a nivel interno, pero tiene cierto mérito. Cuando estoy en Roma, etc. ... me da algo en qué pensar. –

+2

Bueno, su usuario del término "interfaz" en la forma en que lo usa Java muestra una comprensión basada en la terminología y los conceptos de Java, pero no son necesariamente válidos fuera del marco conceptual de Java. Estudiar idiomas es un ejercicio tremendamente útil: aprenda a apreciar las variaciones intelectuales para informar su uso de otros idiomas. – groundhog

+1

(No pensé que el asker directamente enfrentaba a Obj-C contra Java, pero tiene razón en que había algunos matices que sugerían confusión debido al uso de la terminología en los dos idiomas). Objective-C fue en realidad inventado en 1982, y me pareció muy interesante cuando descubrí que Objective-C tenía una influencia no trivial en Java - http://www.virtualschool.edu/objectivec/influenceOnJava.html –

3

Nadie ha explicado por qué las variables de instancia aparecen en el @interface de una clase Objective-C. Los objetos se implementan como estructuras C que contienen las variables de instancia. Cuando se crea una nueva subclase, se define un nuevo tipo de estructura con todos los ivars de la superclase seguidos por los ivars de la nueva clase. La estructura de la superclase debe contener los ivars de su superclase, y luego es "tortugas hasta el final" a una estructura que representa los ivars de la clase base.

En el caso de NSObject (y de hecho Object), la estructura de la clase base sólo contiene un Ivar - un puntero llamado isa a la estructura Class que representa la clase de este objeto. Así que si quería una subclase de esta manera:

@interface GLObject : NSObject { 
    int x; 
} 
@end 

necesito saber cómo hacer una estructura que se parece a:

{ 
    Class isa; 
    int x; 
} 

por lo tanto, el diseño de Ivar de la clase padre (y por inducción cualquier clase) debe formar parte del contrato público de la clase, es decir, parte de su @interface. Puede que no sea lindo, pero ahí está.

+2

De hecho, esto ya no es cierto en el tiempo de ejecución no frágil (Mac OS X e iPhone de 64 bits), que no usa diseños fijos. Al construir para el tiempo de ejecución no frágil, puede agregar ivars implícitamente en el @implementati sobre el uso de la directiva @synthesize. No hay ninguna razón técnica para no permitir un bloque {} ivar en @implementation; La palabra es que esto quedó fuera por razones de tiempo. Errores han sido archivados. –

+0

Correcto, muy cierto. No existe una necesidad técnica ya que la interfaz @ se usa para informar al compilador de los tipos y la membresía necesarios al compilar una fuente que hace referencia a la clase sin causar dependencia en la implementación. Si un ivar es @private, no existe una razón real convincente para su existencia en el bloque @interface que no sea la sintaxis. – groundhog

+0

@both commenters: right. Pensé que introducir múltiples tiempos de ejecución en este momento no era del todo apropiado ;-) –

1

también me he dado cuenta de que no hay nada realmente forzar mis interfaces de orientación al público para contener las variables de instancia ...

@interface Foo : NSObject 
// some methods but no ivars 
+ (Foo*) fooWithBlahBlah 
@end 

Y el archivo de implementación ...

@implementation Foo 

+ (Foo*) fooWithBlahBlah { 
    return [[SomeDerivedFoo alloc] init]; 
} 

@end 

Si se declara SomeDerivedFoo internamente no visible para los clientes ...

@interface SomeDerivedFoo : Foo 
{ 
    // instance vars 
} 
// overrides of methods 

Esto permitiría el componente para exponer una interfaz libre de dependencias ivar. De hecho, este es el modelo que veo en los llamados "Clusters de clase". Esto "se siente" mucho mejor para mí.

Mi gran problema con ivars en la interfaz pública es todo acerca del control de dependencia. Si mis ivars no son más que otras clases de Objective-C, entonces sí puedo salir adelante con las declaraciones, pero en el caso de las estructuras C incorporadas u otros tipos no de clase, tengo que ver la definición de esos tipos y mis clientes dependen ahora de ellos.

Cambiar una variable de instancia no debe hacer que los clientes de mi interfaz vuelvan a compilarse. Una reenlace está bien pero no es una recompilación. En el caso de un marco compartido, ni siquiera se debería volver a vincular. Esto no tiene nada que ver con ningún idioma en particular. Tiene que ver con el aislamiento de los detalles. Esa es una meta bastante universal en mi mente.

Esto es lo que me llevó a mi pregunta original sobre protocolos vs interfaces. Parece que para una biblioteca o marco, lo más "correcto" es proporcionar un conjunto de protocolos junto con algún tipo de clases "Proveedor" o "Fábrica" ​​para vender implementaciones de esos protocolos.

1

Una alternativa si no puede soportar el tiempo de ejecución de 64 bits y sintetizar exclusivamente ivars, es hacer un id ivar en su clase. Si necesita regresar a su clase después del envío y agregar más variables de instancia, puede usar este id como contenedor para ellos.

Esto le permite escribir código menos complejo, mientras le da un margen de maniobra para cambiar las cosas detrás de las espaldas de su cliente sin forzar una recompilación.

Al escribir el ivar como id, no está prometiendo nada al código del cliente (y debe hacerse @private de todos modos).

Me imagino que tener todas sus clases públicas actuando como representantes de implementaciones privadas sería un poco difícil de manejar. Tenga en cuenta que, si lo hace, puede usar el reenvío en tiempo de ejecución para reducir la cantidad de código que tiene que escribir en la clase pública. Ver -[NSObject forwardingTargetForSelector:]. Está documentado en el 10.5 Foundation Release Notes

Cuestiones relacionadas